File Coverage

blib/lib/Mobile/Location.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Mobile::Location;
2              
3             # Location.pm - the mobile agent environment location class.
4             #
5             # Author: Paul Barry, paul.barry@itcarlow.ie
6             # Create: March 2003.
7             # Update: April 2003 - changed to IO::Socket for agent receipt and processing
8             # due to "fork" strangeness on regular sockets.
9             # May 2003 - added support for authentication and encryption.
10             # - added the web-based monitoring service.
11             #
12             # Notes: Version 1.x - unsafe, totally trusting Locations (never released).
13             # Version 2.x - added support to the Location for executing mobile
14             # agents within a restricted Opcode environment.
15             # Version 3.x - adds support for authentication and encryption. This
16             # code assumes that a functioning keyserver is running.
17             # Version 4.x - embeds a web-server to allow for remote monitoring via
18             # the world-wide-web.
19              
20 1     1   9915 use strict;
  1         3  
  1         47  
21              
22 1     1   2320 use Crypt::RSA; # Provides authentication and encryption services.
  0            
  0            
23             use IO::Socket; # OO interface to Socket API.
24             use Socket; # Procedural interface to Socket API.
25             use Sys::Hostname; # Provides a means of determine name of current machine.
26             use HTTP::Daemon; # Provides a basic HTTP server.
27             use HTTP::Status; # Provides support for HTTP status messages.
28             use POSIX 'WNOHANG'; # Provides support for POSIX signals.
29              
30             # Add a signal handler to process and deal with "zombies".
31              
32             $SIG{CHLD} = sub { while ( waitpid( -1, WNOHANG ) > 0 ) { }; };
33              
34             our $VERSION = 4.02;
35              
36             use constant TRUE => 1;
37             use constant FALSE => 0;
38              
39             use constant RUN_LOCATION_DIR => "Location";
40             use constant KEY_SIZE => 1024;
41              
42             use constant RESPONDER_PPORT => '30001';
43             use constant REGISTRATION_PPORT => '30002';
44              
45             use constant SCOOBY_CONFIG_FILE => "$ENV{'HOME'}/.scoobyrc";
46              
47             use constant HTML_DEFAULT_PAGE => "index.html";
48             use constant HTTP_PORT => 8080;
49              
50             use constant LOGFILE => 'location.log';
51              
52             use constant VISIT_SCOOBY => 'Visit the Scooby Website at IT Carlow.

';

53              
54             our $_PWD = ''; # This 'global' contains the current working directory
55             # for the Location instance determined during construction.
56              
57             ##########################################################################
58             # The class constructor is in "new".
59             ##########################################################################
60              
61             sub new {
62              
63             # The Mobile::Location constructor.
64             #
65             # IN: Receives a series of optional name/value pairings.
66             # Port - Protocol port value to accept connections from.
67             # Default value for Port is '2001'.
68             # Debug - set to 1 for STDERR status messages.
69             # Default value for Debug is 0 (off).
70             # Log - set to 1 to enable logging of agents to disk.
71             # Default value for Log is 0 (off).
72             # Ops - a set of Opcodes or Opcode tags, which are
73             # added to Scooby's ALLOWED ops when executing
74             # mobile agents.
75             # Web - set to 1 to enable the logging mechanism and the
76             # creation of a HTTP-based Monitoring Service. The
77             # default is 1 (i.e., ON).
78             #
79             # OUT: Returns a blessed reference to a Mobile::Location object.
80              
81             my ( $class, %arguments ) = @_;
82              
83             my $self = bless {}, $class;
84              
85             $self->{ Port } = $arguments{ Port } || 2001;
86             $self->{ Debug } = $arguments{ Debug } || FALSE;
87             $self->{ Log } = $arguments{ Log } || FALSE;
88             $self->{ Ops } = $arguments{ Ops } || '';
89             $self->{ Web } = $arguments{ Web } || TRUE;
90              
91             # Untaint the PATH by setting it to something really limited.
92              
93             $ENV{'PATH'} = "/bin:/usr/bin";
94              
95             # This next line is part of the standard Perl technique. See 'perlsec'.
96              
97             delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };
98            
99             $_PWD = `pwd`; # XXXXXX: Writing to global! This is tainted.
100             $_PWD =~ /^([-\@\/\w_.]+)$/; # So, we untaint it, using a regex.
101             $_PWD = $1;
102              
103             # Disallow if running this Location as 'root'.
104              
105             die "Location running as ROOT. This is NOT secure (nor allowed)!"
106             unless $> and $^O ne 'VMS';
107              
108             # Work out and remember the IP address of the computer running this Location.
109              
110             my $host = gethostbyname( hostname ) or inet_aton( hostname );
111             $self->{ Host } = inet_ntoa( $host );
112              
113             # Generate and remember a password to use with the PK- and PK+.
114              
115             $self->{ Password } = $0 . $$ . '_Location';
116              
117             # NOTE: A second server is spawned at this stage to handle any
118             # requests from an agent re: the availability of any
119             # required modules within the Perl system running this Location.
120             # See the _check_modules_on_remote subroutine from Devel::Scooby,
121             # as well as the _spawn_network_service and _check_for_modules
122             # subroutines, below.
123              
124             _spawn_network_service( $self->{ Port }+1 );
125              
126             # Create the HTTP-based Monitoring Service.
127              
128             $self->_spawn_web_monitoring_service;
129              
130             return $self;
131             }
132              
133              
134             ##########################################################################
135             # Methods and support subroutines.
136             ##########################################################################
137              
138             sub _logger {
139              
140             # This small routine quickly writes a message to the LOGFILE. Note
141             # that every line written to the LOGFILE is timestamped.
142             #
143             # IN: a message to log.
144             #
145             # OUT: nothing.
146              
147             my $self = shift;
148              
149             # Open the LOGFILE for append >>.
150              
151             open ML_LOGFILE, ">>" . LOGFILE
152             or die "Mobile::Location: unable to append to LOGFILE.\n";
153              
154             print ML_LOGFILE scalar localtime, ": @_\n";
155              
156             close ML_LOGFILE;
157             }
158              
159             sub _logger2 {
160              
161             # This small routine quickly writes a message to the LOGFILE. Note
162             # that every line written to the LOGFILE is timestamped. This code is
163             # the same as "_logger", but for the fact that the location of the
164             # LOGFILE is one-level-up in the directory hierarchy.
165             #
166             # IN: a message to log.
167             #
168             # OUT: nothing.
169              
170             my $self = shift;
171              
172             # Open the LOGFILE (which is one-level-up) for append >>.
173              
174             open ML_LOGFILE, ">>../" . LOGFILE
175             or die "Mobile::Location: unable to append to LOGFILE.\n";
176              
177             print ML_LOGFILE scalar localtime, ": @_\n";
178              
179             close ML_LOGFILE;
180             }
181              
182             sub _build_index_dot_html {
183              
184             # Builds the INDEX.HTML file (used by _start_web_service).
185             #
186             # IN: nothing.
187             #
188             # OUT: nothing (although "index.html" is created).
189              
190             my $self = shift;
191              
192             open HTMLFILE, ">index.html"
193             or die "Mobile::Executive: index.html cannot be written to: $!.\n";
194              
195             print HTMLFILE<
196              
197            
198            
199             Welcome to the Location Web-Based Monitoring Service.
200            
201            
202            

Welcome to the Location Web-Based Monitoring Service

203             end_html
204              
205             print HTMLFILE "Location executing on: " . hostname . ".

";

206             print HTMLFILE "Location date/time: " . localtime() .
207             ". Running on port: " .
208             $self->{ Port } . ".

";

209              
210             print HTMLFILE<
211              
212             Click here to reset the log.
213            

Logging Details

214            
 
215             end_html
216              
217             open HTTP_LOGFILE, LOGFILE
218             or die "Mobile::Location: the LOGFILE is missing - aborting.\n";
219              
220             while ( my $logline = )
221             {
222             print HTMLFILE "$logline";
223             }
224              
225             close HTTP_LOGFILE;
226              
227             print HTMLFILE<
228              
229            
230             end_html
231              
232             print HTMLFILE VISIT_SCOOBY;
233              
234             print HTMLFILE<
235              
236            
237            
238             end_html
239              
240             close HTMLFILE;
241             }
242              
243             sub _build_clearlog_dot_html {
244              
245             # Builds the CLEARLOG.HTML file (used by _start_web_service).
246             #
247             # IN: the name of the just-created backup file.
248             #
249             # OUT: nothing (although "clearlog.html" is created).
250              
251             my $self = shift;
252              
253             my $backup_log = shift;
254              
255             open CLEARLOG_HTML, ">clearlog.html"
256             or die "Mobile::Executive: clearlog.html cannot be written to: $!.\n";
257              
258             print CLEARLOG_HTML<
259              
260            
261            
262             Location Logfile Reset.
263            
264            
265            

Location Logfile Reset

266             The previous logfile has been archived as: $backup_log

267             Return to this Location's main page.

268             end_html
269              
270             print CLEARLOG_HTML VISIT_SCOOBY;
271              
272             print CLEARLOG_HTML<
273              
274            
275            
276             end_html
277              
278             close CLEARLOG_HTML;
279             }
280              
281             sub _start_web_service {
282              
283             # Starts a small web server running on port HTTP_PORT. Provides for some
284             # simple monitoring of the Location.
285             #
286             # IN: nothing.
287             #
288             # OUT: nothing.
289              
290             my $self = shift;
291              
292             my $httpd = HTTP::Daemon->new( LocalPort => HTTP_PORT,
293             Reuse => 1 )
294             or die "Mobile::Location: could not create HTTP daemon on " .
295             HTTP_PORT . ".\n";
296              
297             $self->_logger( "Starting web service on port:", HTTP_PORT ) if $self->{ Web };
298              
299             while ( my $http_client = $httpd->accept )
300             {
301             if ( my $service = $http_client->get_request )
302             {
303             my $request = $service->uri->path;
304              
305             if ( $service->method eq 'GET' )
306             {
307             my $resource;
308            
309             if ( $request eq "/" || $request eq "/index.html" )
310             {
311             $resource = HTML_DEFAULT_PAGE;
312              
313             $self->_build_index_dot_html;
314              
315             $http_client->send_file_response( $resource );
316             }
317             elsif ( $request eq "/clearlog.html" )
318             {
319             # Create a name for the backup log.
320              
321             my $backup_log = "Mobile::Location." . localtime() .
322             "." . $$ . ".log";
323              
324             # Make the backup, delete the LOGFILE, then recreate it.
325              
326             system( "cp", LOGFILE, $backup_log );
327             unlink LOGFILE;
328              
329             $self->_logger( "Mobile::Location: log reset." ) if $self->{ Web };
330              
331             $self->_build_clearlog_dot_html( $backup_log );
332              
333             $http_client->send_file_response( "clearlog.html" );
334             }
335             else
336             {
337             $http_client->send_error( RC_NOT_FOUND );
338             }
339             }
340             else
341             {
342             $http_client->send_error( RC_METHOD_NOT_ALLOWED );
343             }
344             }
345             }
346             continue
347             {
348             $http_client->close;
349             undef( $http_client );
350             }
351             }
352              
353             sub _register_with_keyserver {
354              
355             # Create a PK+ and PK- for this server, storing the PK+ in the
356             # keyserver, and retaining the PK- in memory (as part of the objects
357             # state). Note: a new key-pair is generated with each invocation.
358             #
359             # IN: nothing. (Other than the object reference, of course).
360             #
361             # OUT: nothing.
362            
363             my $self = shift;
364              
365             # Generate the PK+ and PK-. Store the PK- in the object's state.
366              
367             my $rsa = new Crypt::RSA;
368              
369             my $id = $self->{ Host } . ":" . $self->{ Port } . " Location";
370              
371             warn "This location is generating a PK+/PK- pairing.\n" if $self->{ Debug };
372              
373             my ( $public, $private ) =
374             $rsa->keygen(
375             Identity => $id,
376             Size => KEY_SIZE,
377             Password => $self->{ Password },
378             Verbosity => FALSE
379             ) or die $rsa->errstr, "\n";
380              
381             warn "Pairing Generated.\n" if $self->{ Debug };
382              
383             $self->_logger( "Location's PK+/PK- pairing generated." ) if $self->{ Web };
384              
385             # Remember the PK- in the object's state.
386              
387             $self->{ PrivateKey } = $private;
388              
389             # Write the PK+ to an appropriately named disk-file.
390              
391             my $pub_fn = $self->{ Host } . "." . $self->{ Port } . ".public";
392              
393             $self->_logger( "Writing PK+ to: $pub_fn." ) if $self->{ Web };
394              
395             $public->write( Filename => $pub_fn );
396              
397             # Determine the KEYSERVER address from the .scoobyrc file.
398              
399             open KEYFILE, SCOOBY_CONFIG_FILE
400             or die "Mobile::Location: unable to access ~/.scoobyrc. Does it exist?\n";
401              
402             my $keyline = ;
403              
404             close KEYFILE;
405              
406             # Note: format of 'rc' file is very strict. No spaces!
407             $keyline =~ /^KEYSERVER=(.+)/;
408              
409             $self->{ KeyServer } = $1;
410              
411             # Now that we know the address of the keyserver, we can register the PK+ of this
412             # Location with the keyserver. We read the PK+ from the just-created disk-file.
413              
414             $self->_logger( "Determined keyserver address as:", $self->{ KeyServer } ) if $self->{ Web };
415              
416             open KEYFILE, "$pub_fn"
417             or die "Mobile::Location: KEYFILE does not exist: $!.\n";
418              
419             my @entire_keyfile = ;
420              
421             close KEYFILE;
422              
423             my $keysock_obj = IO::Socket::INET->new( PeerAddr => $self->{ KeyServer },
424             PeerPort => REGISTRATION_PPORT,
425             Proto => 'tcp' );
426              
427             if ( !defined( $keysock_obj ) )
428             {
429             die "Mobile::Location: could not create socket object to key server: $!.\n";
430             }
431             print $keysock_obj $self->{ Port }, "\n";
432             print $keysock_obj @entire_keyfile;
433              
434             $keysock_obj->close;
435              
436             $self->_logger( "Location registered with keyserver." ) if $self->{ Web };
437             }
438              
439             sub start_concurrent {
440              
441             # Start a passive server/location that executes concurrently. For
442             # each relocation request, a child process is spawned to process it.
443             #
444             # IN: nothing.
445             #
446             # OUT: nothing.
447             #
448             # This method is never returned from. Remember: servers are PERMANENT.
449            
450             my $self = shift;
451              
452             my $listening_socket = IO::Socket::INET->new( LocalPort => $self->{ Port },
453             Listen => SOMAXCONN,
454             Proto => 'tcp',
455             Reuse => TRUE );
456              
457             if ( !defined( $listening_socket ) )
458             {
459             die "Mobile::Location: unable to bind to listening socket: $!.\n";
460             }
461              
462             $self->_logger( "Location (concurrent) starting on port:", $self->{ Port } ) if $self->{ Web };
463              
464             warn "Location starting up on port: " . $self->{ Port } . ".\n" if $self->{ Debug };
465            
466             $self->_register_with_keyserver;
467            
468             while ( TRUE ) # i.e., FOREVER, as servers are permanent.
469             {
470             next unless my $from_socket = $listening_socket->accept;
471              
472             next if my $child = fork;
473              
474             if ( $child == FALSE )
475             {
476             $self->_logger( "Servicing client from:",
477             inet_ntoa( $from_socket->peeraddr ) ) if $self->{ Web };
478              
479             $listening_socket->close;
480             $self->_service_client( $from_socket );
481             exit FALSE;
482             }
483              
484             $from_socket->close;
485             }
486             }
487            
488             sub start_sequential {
489              
490             # Start a passive server/location that executes sequentially.
491             #
492             # IN: nothing.
493             #
494             # OUT: nothing.
495             #
496             # This method is never returned from. Remember: servers are PERMANENT.
497              
498             my $self = shift;
499              
500             my $listening_socket = IO::Socket::INET->new( LocalPort => $self->{ Port },
501             Listen => SOMAXCONN,
502             Proto => 'tcp',
503             Reuse => TRUE );
504              
505             if ( !defined( $listening_socket ) )
506             {
507             die "Mobile::Location: unable to bind to listening socket: $!.\n";
508             }
509              
510             $self->_logger( "Location (sequential) starting on port:", $self->{ Port } ) if $self->{ Web };
511              
512             warn "Location starting up on port: " . $self->{ Port } . ".\n" if $self->{ Debug };
513            
514             $self->_register_with_keyserver;
515            
516             # Servers are PERMANENT.
517              
518             while ( TRUE )
519             {
520             next unless my $from_socket = $listening_socket->accept;
521              
522             $self->_logger( "Servicing client from:",
523             inet_ntoa( $from_socket->peeraddr ) ) if $self->{ Web };
524              
525             $self->_service_client( $from_socket );
526             }
527             }
528              
529             sub _service_client {
530              
531             # Service the receipt (and re-execution) of a mobile agent on
532             # this Location.
533             #
534             # IN: A socket object to communicate with/on.
535             #
536             # OUT: nothing.
537              
538             my $self = shift;
539              
540             my $socket_object = shift;
541              
542             my $tmp_fn = <$socket_object>; # The received filename.
543             chomp( $tmp_fn );
544              
545             # We just want the name-part, so a little regex magic gives it to us.
546              
547             $tmp_fn = ( split /\//, $tmp_fn )[-1];
548              
549             my $tmp_linenum = <$socket_object>; # The received line number.
550             chomp( $tmp_linenum );
551              
552             my $data = '';
553              
554             # Receive the signature and mobile agent code.
555              
556             while ( my $chunk = <$socket_object> )
557             {
558             $data = $data . $chunk;
559             }
560              
561             # We need to split out the signature from the $data so that we can verify it.
562              
563             ( my $agent_signature, $data ) = split /\n--end-sig--\n/, $data;
564              
565             # We need to verify the signature. To do this, we need to retrieve
566             # the appropriate PK+ from the keyserver.
567              
568             my $key_srv_sock = IO::Socket::INET->new(
569             PeerAddr => $self->{ KeyServer },
570             PeerPort => RESPONDER_PPORT,
571             Proto => 'tcp'
572             );
573              
574             if ( !defined( $key_srv_sock ) )
575             {
576             $self->_logger( "Unable to create a verify socket." ) if $self->{ Web };
577            
578             die "Mobile::Location: unable to create a verify socket to keyserver: $!.\n";
579             }
580              
581             my $agent_ip = $socket_object->peerhost;
582             my $agent_port = $socket_object->peerport;
583              
584             print $key_srv_sock "$agent_ip\n";
585             print $key_srv_sock $agent_port;
586              
587             $key_srv_sock->shutdown( 1 );
588              
589             my $verify_data = '';
590            
591             while ( my $verify_chunk = <$key_srv_sock> )
592             {
593             $verify_data = $verify_data . $verify_chunk;
594             }
595            
596             $key_srv_sock->close;
597            
598             # This splits the signature and data on the SIGNATURE_DELIMITER
599             # pattern as used by the keyserver.
600              
601             ( my $verify_signature, $verify_data ) = split /\n--end-sig--\n/, $verify_data;
602              
603             if ( $verify_signature eq "NOSIG" )
604             {
605             $self->_logger( "WARNING: The keyserver returned NOSIG." ) if $self->{ Web };
606            
607             # We need to abort, as the keyserver does not have the requested
608             # signature. This is bad.
609              
610             $socket_object->close;
611              
612             exit 0; # Short circuit.
613             }
614              
615             open VERIFY_FILE, ">$agent_ip.$agent_port.public"
616             or die "Mobile::Location: could not create verify key file: $!\n";
617              
618             print VERIFY_FILE $verify_data;
619              
620             close VERIFY_FILE;
621              
622             my $agent_pkplus = new Crypt::RSA::Key::Public(
623             Filename => "$agent_ip.$agent_port.public"
624             );
625              
626             my $rsa = new Crypt::RSA;
627              
628             my $verify = $rsa->verify(
629             Message => $data,
630             Signature => $agent_signature,
631             Key => $agent_pkplus,
632             Armour => TRUE
633             );
634              
635             if ( !$verify )
636             {
637             $self->_logger( "WARNING: could not verify signature for:",
638             inet_ntoa( $socket_object->peeraddr ),
639             "using $agent_ip/$agent_port." ) if $self->{ Web };
640              
641             die "Mobile::Location: could not verify signature of received mobile agent. Aborting ... \n";
642             }
643              
644             $self->_logger( "Signature verified for $agent_ip/$agent_port." ) if $self->{ Web };
645              
646             # Remove the agents PK+ keyfile, as we no longer need it.
647              
648             unlink "$agent_ip.$agent_port.public";
649              
650             # At this stage, we have a mobile agent that is encrypted using the PK+
651             # of this Location, and we have verified the signature to be correct.
652             # We use this Location's PK- to decrypt it.
653              
654             my $plaintext = $rsa->decrypt(
655             Cyphertext => $data,
656             Key => $self->{ PrivateKey },
657             Armour => TRUE
658             );
659              
660             if ( !defined( $plaintext ) )
661             {
662             $self->_logger( "WARNING: unable to decrypt Cyphertext for: $agent_ip/$agent_port." ) if $self->{ Web };
663              
664             die "Mobile::Location: decryption errors - aborting.\n";
665             }
666              
667             # We have a plaintext representation of the mobile agent, which
668             # we turn back into an array of lines.
669              
670             my @entire_thing = split /\n/, $plaintext;
671              
672             # Add a newline to each of the "lines" in @entire_thing.
673              
674             foreach my $line ( @entire_thing )
675             {
676             $line = $line . "\n";
677             }
678              
679             # Ensure the Location is in the correct STARTUP directory.
680              
681             chdir $_PWD;
682              
683             # We enter the run-time directory if it exists.
684              
685             if ( -e RUN_LOCATION_DIR )
686             {
687             chdir( RUN_LOCATION_DIR );
688             }
689             else # Or, if it does NOT exist, we create it then change into it.
690             {
691             mkdir( RUN_LOCATION_DIR );
692             chdir( RUN_LOCATION_DIR );
693             }
694              
695             # As we are now in the run-time directory, we continue with the relocation.
696              
697             if ( $self->{ Log } )
698             {
699             my $logname = "last_agent_" . $$ . ".log"; # Note use of PID.
700            
701             # Put a copy of the mobile agent into the log file.
702              
703             my $logOK = open AGENTLOGFILE, ">$logname"
704             or warn "Mobile::Location: could not open log file: $!.\n";
705              
706             print AGENTLOGFILE @entire_thing if defined $logOK;
707              
708             close AGENTLOGFILE if defined $logOK;
709              
710             $self->_logger2( "Received agent logged to: $logname." ) if $self->{ Web };
711             }
712              
713             # Untaint the filename received from Scooby, using a regex.
714              
715             $tmp_fn =~ /^([-\@\w_.]+)$/;
716             $tmp_fn = $1;
717            
718             # Create the "mutated" agent on the local storage.
719              
720             open FILETOCHECK, ">$tmp_fn"
721             or die "Location::Mobile: could not create agent disk-file: $!:";
722            
723             my $label = _generate_label( $tmp_fn, $tmp_linenum );
724            
725             # Start processing the agent one "line" at a time.
726              
727             my $chunk = shift @entire_thing;
728              
729             # Print the "magic" first line.
730              
731             print FILETOCHECK $chunk;
732              
733             # # Add the Opcode mask to the code.
734             #
735             # print FILETOCHECK "\nuse ops qw( " .
736             #
737             # # Basic operation mask - relocating to a single Location.
738             #
739             # 'aassign add aelem av2arylen ' .
740             # 'backtick ' .
741             # 'caller chdir chomp chop closedir concat const ' .
742             # 'defined die ' .
743             # 'enter entereval enteriter entersub eq ' .
744             # 'ftdir fteexec ftewrite ' .
745             # 'gelem goto grepstart gv ' .
746             # 'helem ' .
747             # 'iter ' .
748             # 'join ' .
749             # 'last leaveeval leaveloop leavesub lstat ' .
750             # 'method method_named ' .
751             # 'ne negate next not null ' .
752             # 'open_dir ' .
753             # 'padany pop push pushmark ' .
754             # 'readdir refgen require return rv2av rv2cv rv2gv rv2hv rv2sv ' .
755             # 'sassign scalar seq shift sne split stat stringify stub substr ' .
756             # 'undef unshift unstack ' .
757             #
758             # # Relocating to multiple Locations (requires more operations).
759             # # Most of these are needed by Carp.pm, which is used by IO::Socket
760             # # (among other modules).
761             #
762             # 'anonhash anonlist ' .
763             # 'exists ' .
764             # 'keys ' .
765             # 'gt ' .
766             # 'length lt ' .
767             # 'mapstart ' .
768             # 'ord ' .
769             # 'postinc predec preinc ' .
770             # 'redo ref ' .
771             # 'sprintf subtract ' .
772             # 'wantarray ' .
773             #
774             # # Adding the ops required by Crypt::RSA and its support modules.
775             #
776             # 'anoncode ' .
777             # 'bless bit_and bit_or bit_xor ' .
778             # 'chr close complement ' .
779             # 'divide delete dofile ' .
780             # 'each enterwrite eof ' .
781             # 'fcntl fileno flip flop formline fteread ftfile ftis ftsize ' .
782             # 'ge getc ' .
783             # 'hex '
784             # 'int index ioctl ' .
785             # 'lc le left_shift lslice '
786             # 'modulo multiply '
787             # 'oct open '
788             # 'pack padsv postdec pow print prtf '
789             # 'quotemeta ' .
790             # 'rand read readline repeat reverse regcreset ' .
791             # 'select splice srand sysread syswrite '
792             # 'tell tie trans truncate '
793             # 'uc unpack '
794             # 'values vec '
795             # 'warn '
796             # 'xor '
797             #
798             # $self->{ Ops } . " );\n\n"; # Forces safety.
799             #
800              
801             # Insert the GOTO label line.
802              
803             print FILETOCHECK "goto $label;\n";
804              
805             # We re-initialize the line counter.
806              
807             my $line_counter = 2;
808              
809             # Process the rest of the agent, one "line" at a time.
810              
811             while ( $chunk = shift @entire_thing )
812             {
813             if ( $line_counter == $tmp_linenum ) # We are at the 'next' line.
814             {
815             # Insert a 'label' statement before the next instruction.
816              
817             print FILETOCHECK "$label:\n1;\n";
818              
819             print FILETOCHECK "use Mobile::Executive;\n\n";
820             }
821             print FILETOCHECK $chunk;
822             $line_counter++;
823             }
824            
825             close FILETOCHECK;
826              
827             # Note: The agent now exists on the local run-time storage of this Location.
828            
829             $self->_logger2( "Received $tmp_fn from", $socket_object->peerhost,
830             " next line: $tmp_linenum." ) if $self->{ Web };
831              
832             warn "Received $tmp_fn from ",
833             $socket_object->peerhost,
834             "; next line: $tmp_linenum.\n" if $self->{ Debug };
835              
836             # Construct the command-line that will continue to execute the agent.
837              
838             my $cmd = "perl -d:Scooby " . "$tmp_fn";
839            
840             # Close the socket as we are now finished with it.
841              
842             close $socket_object
843             or warn "Mobile::Location: close failed: $!.\n";
844              
845             # Continue to execute the agent at this location.
846              
847             warn "Continuing to execute agent: $cmd.\n" if $self->{ Debug };
848            
849             $self->_logger2( "Continuing to execute mobile agent: $cmd." ) if $self->{ Web };
850              
851             my $results = qx( $cmd );
852            
853             print "$results" if $results ne '';
854             }
855              
856             sub _spawn_web_monitoring_service {
857              
858             # Creates a subprocess to run the web-based monitoring service.
859             #
860             # IN: nothing.
861             #
862             # OUT: nothing.
863              
864             my $self = shift;
865              
866             my $child_pid = fork;
867              
868             die "No spawned web-based monitoring service: $!.\n" unless defined( $child_pid );
869              
870             if ( $child_pid == FALSE )
871             {
872             # This is the CHILD code, which creates a server on "Port+1" and
873              
874             $self->_start_web_service if $self->{ Web };
875              
876             exit 0;
877             }
878             }
879              
880             ##########################################################################
881             # These are not methods, they're support subroutines.
882             ##########################################################################
883              
884             sub _generate_label {
885              
886             # Generate a unique label string.
887             #
888             # IN: A filename and a line number.
889             # Note: These values are combined with the time to produce a
890             # random (and hopefully unique) label.
891             #
892             # OUT: An appropriately formatted label.
893              
894             my $fn = shift;
895             my $ln = shift;
896              
897             my $tm = time;
898              
899             # Remove any unwanted characters from the filename.
900              
901             $fn =~ s/[^a-zA-Z0-9]//;
902              
903             return ( 'LABEL_' . $fn . $ln . $tm );
904             }
905              
906             sub _check_for_modules {
907              
908             # Given a list of module classes, check to see if they exist within this
909             # Location's Perl environment.
910             #
911             # IN: A list of fully-qualified (one or more) module names.
912             # A "fully-qualified module name" is "Devel::Scooby", as
913             # opposed to just "Scooby".
914             #
915             # OUT: A list of modules NOT found. An empty list signals SUCCESS.
916              
917             my @mods_to_check = @_; # Taken from IN.
918              
919             my @list_of_not_found = (); # Will be used as OUT.
920              
921             foreach my $mod ( @mods_to_check )
922             {
923             # Untaint the $mod values prior to their use, using a regex.
924              
925             $mod =~ /^([\w\d:_]+)$/;
926             $mod = $1;
927              
928             eval "require $mod;";
929             if ( $@ )
930             {
931              
932             # The module does not exist within this Perl!!
933              
934             push @list_of_not_found, $mod;
935             }
936             }
937              
938             return @list_of_not_found;
939             }
940              
941             sub _spawn_network_service {
942              
943             # Spawn a sub-process, running at protocol port number "$self->{ Port }+1"
944             # to respond to an agent's query re: required classes.
945             #
946             # IN: The protocol port to start the service on.
947             #
948             # OUT: nothing.
949              
950             my $port = shift;
951              
952             # Untaint the value for $port, as it can be initialized from
953             # the command-line, and is therefore TAINTED.
954              
955             $port =~ /^(\d+)$/;
956             $port = $1;
957              
958             my $child_pid = fork;
959              
960             die "No spawned network service: $!.\n" unless defined( $child_pid );
961              
962             # This child code never ends, as servers are PERMANENT.
963              
964             if ( $child_pid == FALSE )
965             {
966             # This is the CHILD code, which creates a server on "Port+1" and
967             # listens for requests from a remote mobile agent.
968              
969             my $trans_serv = getprotobyname( 'tcp' );
970             my $local_addr = sockaddr_in( $port, INADDR_ANY );
971              
972             socket( TCP_SOCK, PF_INET, SOCK_STREAM, $trans_serv )
973             or die "Mobile::Location: socket creation failed: $!.\n";
974             setsockopt( TCP_SOCK, SOL_SOCKET, SO_REUSEADDR, 1 )
975             or warn "Mobile::Location: could not set socket option: $!.\n";
976             bind( TCP_SOCK, $local_addr )
977             or die "Mobile::Location: bind to address failed: $!.\n";
978             listen( TCP_SOCK, SOMAXCONN )
979             or die "Mobile::Location: listen couldn't: $!.\n";
980              
981             my $from_who;
982              
983             while ( $from_who = accept( CHECK_MOD_SOCK, TCP_SOCK ) )
984             {
985             # Switch on AUTO-FLUSHING.
986              
987             my $previous = select CHECK_MOD_SOCK;
988             $| = 1;
989             select $previous;
990              
991             my $data = '';
992            
993             # Get the list of modules from the other Location.
994              
995             while ( my $chunk = )
996             {
997             $data = $data . $chunk;
998             }
999              
1000             my @modules = split / /, $data;
1001              
1002             my @list = _check_for_modules( @modules );
1003              
1004             if ( @list )
1005             {
1006             print CHECK_MOD_SOCK "NOK: @list";
1007             }
1008             else
1009             {
1010             print CHECK_MOD_SOCK "OK";
1011             }
1012              
1013             close CHECK_MOD_SOCK
1014             or warn "Mobile::Location: close failed: $!.\n";
1015             }
1016              
1017             close TCP_SOCK; # This code may never be reached. It only
1018             # executes if the call to "accept" fails.
1019             }
1020              
1021             # This is the parent process code. That is, the value of
1022             # $child_pid is defined and is greater than 0.
1023             }
1024              
1025             1; # As it is required by Perl.
1026              
1027             ##########################################################################
1028             # Documentation starts here.
1029             ##########################################################################
1030              
1031             =pod
1032              
1033             =head1 NAME
1034              
1035             "Mobile::Location" - a class that provides for the creation of Scooby mobile agent environments (aka Location, Site or Place).
1036              
1037             =head1 VERSION
1038              
1039             4.0x (the v1.0x, v2.0x and v3.0x series were never released).
1040              
1041             =head1 SYNOPSIS
1042              
1043             use Mobile::Location;
1044              
1045             my $location = Mobile::Location->new;
1046              
1047             $location->start_sequential;
1048              
1049             or
1050              
1051             $location->start_concurrent;
1052              
1053              
1054             =head1 SOME IMPORTANT NOTES FOR LOCATION WRITERS
1055              
1056             1. Never, ever run a Location as 'root'. If you do, this module will die. Running as 'root' is a serious security risk, as a mobile agent is foreign code that you are trusting to execute in a non-threatening way on your computer. (Can you spell the word 'v', 'i', 'r', 'u', 's'?!?)
1057              
1058             2. The B class executes mobile agents within a restricted environment. See the B argument to the B method, below, for more details.
1059              
1060             3. Never, ever run a Location on the same machine that is acting as your keyserver (it's a really bad idea, so don't even think about it).
1061              
1062             =head1 DESCRIPTION
1063              
1064             Part of the Scooby mobile agent machinery, the B class provides a convenient abstraction of a mobile agent environment. Typical usage is as shown in the B section above. This class allows for the creation of a passive, TCP-based mobile agent Location.
1065              
1066             =head1 Overview
1067              
1068             Simply create an object of type B with the B method. To start a sequential server, use the B method. To start a concurrent server, use the B method.
1069              
1070             =head1 Construction and initialization
1071              
1072             Create a new instance of the B object by calling the B method:
1073              
1074             =over 4
1075              
1076             my $location = Mobile::Location->new;
1077              
1078             =back
1079              
1080             Optional named parameters (with default values) are:
1081              
1082             =over 4
1083              
1084             B - set to 1 to receive STDERR status messages from the object.
1085              
1086             B - sets the protocol port number to accept connections on.
1087              
1088             B - set to 1 to instruct the Location to log the received mobile agent to disk prior to performing any mutation. The name of the logged agent is "last_agent_PID.log", where PID is the process identifier of the Location. On sequential Locations, the PID is always the same value for each received agent. On concurrent Locations, the PID is the PID of the child process that services the relocation/re-execution, so it is always different for each received agent (so watch your disk space). It is often useful to switch this option on (by setting Log to 1) when debugging. Note that the received mobile agent persists on the Location's local disk storage.
1089              
1090             B - add a list of Opcodes to the Opcode mask that is in effect when the mobile agent executes. Study the standard B and B modules for details on Opcodes and how they are set. One way to secure your Location against attack is to ensure that the Opcodes in effect while a mobile agent executes are "safe". This is NOT an easy task, as protecting the mobile agent environment from malicious mobile agents is never easy. Note that the default set of Opcodes in effect are enough to allow the relocation mechanism to execute. B: if the mobile agent uses a operation not allowed by the Opcode mask, it is killed and stops executing. The Location continues to execute, and waits passively for the next mobile agent to arrive. The default set of enabled Opcodes is restrictive. Provide a space-delimited list of Opcodes to this argument to add to the list of allowed opcodes. NOTE: this functionality is currently B due to conflicts/incompatibilities with the current version of Crypt::RSA (version 1.50).
1091              
1092             B - turns on the HTTP-based Monitoring Service running on port 8080 (HTTP_PORT), thus enabling remote monitoring of the Locations current status. It also logs interactions with this Location into 'location.log' (LOGFILE). Set to 0 to disable this behaviour.
1093              
1094             =back
1095              
1096             Note that any received mobile agent executes in a directory called "Location", which will be created (if needs be) in the directory that houses this Location. Any "logs" are also created in the "Location" directory.
1097              
1098             A constructor example is:
1099              
1100             =over 4
1101              
1102             my $place = Mobile::Location->new( Port => 5555, Debug => 1 );
1103              
1104             =back
1105              
1106             creates an object that will display all STDERR status messages, and use protocol port number 5555 for connections. Logging of received agents to disk is off. The standard Opcode mask is in effect. And logging to disk is on, as is the HTTP server.
1107              
1108             When the Location is constructed with B, a second network service is created, running at protocol port number B. In the example above, this second network service would run at protocol port number 5556. When sent the names of a set of Perl classes (e.g., Data::Dumper, HTTP::Request, Net::SNMP and the like), this service checks to see if the classes are available to the locally installed Perl installation. This allows B to determine whether or not relocation is worthwhile prior to an attempted relocation. The B module tries to determines the list of classes used by any mobile agent and communicates with this second network service "in the background". This all happens automatically, so the mobile agent programmer does not need to worry about it, as B only complains when a module does not exist on a remote Location. That said, the administrator of the Location does need to be aware of this second network service. To confirm that the Location and the second network service are up-and-running use the B command-line utility (on Linux). The two "listening" services should appear in netstat's output.
1109              
1110             Note: If a Location crashes (or is killed), the second network service can sometimes keeps running. After all, it is a separate process (albeit a child of the original). Trying to restart the Location results in an "bind to address failed" error message. Use the B command to identify the Perl interpreter that is executing and kill it with B, where B is the process ID of the child process's Perl interpreter.
1111              
1112             =head1 Class and object methods
1113              
1114             =over 4
1115              
1116             =item B
1117              
1118             Start the location as a passive server, which operates concurrently. Once connected to a client, the server forks another process to receive and continue executing a mobile agent. This is the preferred method to use when there exists the potential to have an agent execute for a long period of time.
1119              
1120             =item B
1121              
1122             Start the location as a passive server, which operates sequentially. Once connected to a client, the server sequentially processes the receipt and continued executing of a mobile agent. This is OK if the agent is quick and not processor intensive. If the agent has the potential to execute for a long period of time, use the B method instead. This may also be of use within environments that place a restriction on the use of B.
1123              
1124             =back
1125              
1126             =head1 Internal methods/subroutines
1127              
1128             The following list of subroutines are used within the class to provide support services to the class methods. These subroutines should not be invoked through the object (and in some cases, cannot be invoked through the object).
1129              
1130             =over 4
1131              
1132             =item B<_generate_label>
1133              
1134             Takes a filename and line number, then combines them with the current time to produce a random, unique label.
1135              
1136             =item B<_check_for_modules>
1137              
1138             Given a list of module names, checks to see if the Location's Perl system has the module installed or not.
1139              
1140             =item B<_spawn_network_service>
1141              
1142             Used by the B constructor to spawn the Port+1 network service which listens for a list of modules names from a mobile agent, then checks for their existence within the locally installed Perl system.
1143              
1144             =item B<_service_client>
1145              
1146             Given a socket object (and the instances init data), service the relocation of a Scooby mobile agent.
1147              
1148             =item B<_register_with_keyserver>
1149              
1150             Creates a PK+ and PK- value for the server, storing the PK+ in the keyserver, and the PK- in the object's state.
1151              
1152             =item B<_logger> and B<_logger2>
1153              
1154             Logs a message to the LOGFILE.
1155              
1156             =item B<_build_index_dot_html>
1157              
1158             Builds the INDEX.HTML page for use by the HTTP-based Monitoring Service.
1159              
1160             =item B<_build_clearlog_dot_html>
1161              
1162             Builds the CLEARLOG.HTML page for use by the HTTP-based Monitoring Service.
1163              
1164             =item B<_start_web_service>
1165              
1166             Starts a small web server running at port 8080 (HTTP_PORT), and uses the two "_build_*" routines just described.
1167              
1168             =item B<_spawn_web_monitoring_service>
1169              
1170             Creates a subprocess and starts the web server.
1171              
1172             =back
1173              
1174             =head1 SEE ALSO
1175              
1176             The B module (for creating mobile agents), as well as B (for running mobile agents).
1177              
1178             The Scooby Website: B.
1179              
1180             =head1 AUTHOR
1181              
1182             Paul Barry, Institute of Technology, Carlow in Ireland, B, B.
1183              
1184             =head1 COPYRIGHT
1185              
1186             Copyright (c) 2003, Paul Barry. All Rights Reserved.
1187              
1188             This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
1189