File Coverage

blib/lib/HoneyClient/Util/SOAP.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 24 26 92.3


line stmt bran cond sub pod time code
1             #######################################################################
2             # Created on: Apr 20, 2006
3             # Package: HoneyClient::Util::SOAP
4             # File: SOAP.pm
5             # Description: Generic interface to server and client SOAP operations.
6             #
7             # CVS: $Id: SOAP.pm 773 2007-07-26 19:04:55Z kindlund $
8             #
9             # @author ttruong, kindlund
10             #
11             # Copyright (C) 2007 The MITRE Corporation. All rights reserved.
12             #
13             # This program is free software; you can redistribute it and/or
14             # modify it under the terms of the GNU General Public License
15             # as published by the Free Software Foundation, using version 2
16             # of the License.
17             #
18             # This program is distributed in the hope that it will be useful,
19             # but WITHOUT ANY WARRANTY; without even the implied warranty of
20             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21             # GNU General Public License for more details.
22             #
23             # You should have received a copy of the GNU General Public License
24             # along with this program; if not, write to the Free Software
25             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26             # 02110-1301, USA.
27             #
28             #######################################################################
29              
30             =pod
31              
32             =head1 NAME
33              
34             HoneyClient::Util::SOAP - Perl extension to provide a generic interface
35             to all client and server SOAP operations, for any HoneyClient module.
36              
37             =head1 VERSION
38              
39             This documentation refers to HoneyClient::Util::SOAP version 0.98.
40              
41             =head1 SYNOPSIS
42              
43             =head2 CREATING A SOAP SERVER
44              
45             use HoneyClient::Util::SOAP qw(getServerHandle);
46              
47             # Create a new SOAP server, using default values.
48             my $daemon = getServerHandle();
49              
50             # In the previous example, if this code were listed in package
51             # "A::B", where the package's global configuration variables
52             # for "address" and "port" was "localhost" and "8080" respectively
53             # (as listed in etc/honeyclient.conf), then the corresponding
54             # SOAP server URL would be:
55             #
56             # http://localhost:8080/A/B
57              
58             # Create a new SOAP server, using specific address/ports.
59             my $daemon = getServerHandle(address => "localhost",
60             port => 9090);
61              
62             # Create a new SOAP server, using the specific "A::B::C" namespace.
63             my $daemon = getServerHandle(address => "localhost",
64             port => 9090,
65             namespace => "A::B::C");
66              
67             # When you're ready to start listening for connections, call
68             # the handle() function, like:
69             $daemon->handle();
70              
71             # Note: Remember, this handle() call *will* block. If you have
72             # any other code you want to execute after calling handle(), then
73             # it is suggested that you call handle() from within a child
74             # process or thread.
75              
76             =head2 CREATING A SOAP CLIENT
77              
78             use HoneyClient::Util::SOAP qw(getClientHandle);
79              
80             # Create a new SOAP client, to talk to the HoneyClient::Manager::VM
81             # module.
82             my $stub = getClientHandle(namespace => "HoneyClient::Manager::VM");
83              
84             # Create a new SOAP client, to talk to the HoneyClient::Agent::Driver
85             # module
86             my $stub = getClientHandle(namespace => "HoneyClient::Agent::Driver");
87              
88             # Create a new SOAP client, to talk to the HoneyClient::Manager::VM
89             # module on localhost:9090.
90             my $stub = getClientHandle(namespace => "HoneyClient::Agent::Driver",
91             address => "localhost",
92             port => 9090);
93            
94             # Create a new SOAP client, to talk to the HoneyClient::Manager::VM
95             # module on localhost:9090, using a custom fault handler.
96             $faultHandler = sub { die "Something bad happened!"; };
97             my $stub = getClientHandle(namespace => "HoneyClient::Agent::Driver",
98             address => "localhost",
99             port => 9090,
100             fault_handler => $faultHandler);
101              
102             # Create a new SOAP client, as a callback to this package.
103             my $stub = getClientHandle();
104              
105             =head1 DESCRIPTION
106              
107             This library allows any HoneyClient module to quickly create new
108             SOAP servers or interact with existing ones, by using ports and
109             protocols that are globally defined within a configuration file,
110             rather than using hard coded values within each module.
111              
112             This library makes extensive use of the SOAP::Lite module.
113              
114             =cut
115              
116             package HoneyClient::Util::SOAP;
117              
118 1     1   403518 use strict;
  1         4  
  1         63  
119 1     1   5 use warnings;
  1         2  
  1         49  
120 1     1   6 use Carp ();
  1         2  
  1         191  
121              
122             #######################################################################
123             # Module Initialization #
124             #######################################################################
125              
126             BEGIN {
127             # Defines which functions can be called externally.
128 1     1   7 require Exporter;
129 1         3 our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
130              
131             # Set our package version.
132 1         2 $VERSION = 0.98;
133              
134 1         20 @ISA = qw(Exporter);
135              
136             # Symbols to export automatically
137 1         3 @EXPORT = qw(getServerHandle getClientHandle);
138              
139             # Items to export into callers namespace by default. Note: do not export
140             # names by default without a very good reason. Use EXPORT_OK instead.
141             # Do not simply export all your public functions/methods/constants.
142              
143             # This allows declaration use HoneyClient::Util::SOAP ':all';
144             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
145             # will save memory.
146              
147 1         6 %EXPORT_TAGS = (
148             'all' => [ qw(getServerHandle getClientHandle) ],
149             );
150              
151             # Symbols to autoexport (when qw(:all) tag is used)
152 1         1 @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
  1         4  
153              
154 1         107 $SIG{PIPE} = 'IGNORE'; # Do not exit on broken pipes.
155             }
156             our (@EXPORT_OK, $VERSION);
157              
158             =pod
159              
160             =begin testing
161              
162             # Make sure Log::Log4perl loads
163             BEGIN { use_ok('Log::Log4perl', qw(:nowarn))
164             or diag("Can't load Log::Log4perl package. Check to make sure the package library is correctly listed within the path.");
165            
166             # Suppress all logging messages, since we need clean output for unit testing.
167             Log::Log4perl->init({
168             "log4perl.rootLogger" => "DEBUG, Buffer",
169             "log4perl.appender.Buffer" => "Log::Log4perl::Appender::TestBuffer",
170             "log4perl.appender.Buffer.min_level" => "fatal",
171             "log4perl.appender.Buffer.layout" => "Log::Log4perl::Layout::PatternLayout",
172             "log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n",
173             });
174             }
175             require_ok('Log::Log4perl');
176             use Log::Log4perl qw(:easy);
177              
178             # Make sure the module loads properly, with the exportable
179             # functions shared.
180             BEGIN { use_ok('HoneyClient::Util::SOAP', qw(getServerHandle getClientHandle)) or diag("Can't load HoneyClient::Util::SOAP package. Check to make sure the package library is correctly listed within the path."); }
181             require_ok('HoneyClient::Util::SOAP');
182             can_ok('HoneyClient::Util::SOAP', 'getServerHandle');
183             can_ok('HoneyClient::Util::SOAP', 'getClientHandle');
184             use HoneyClient::Util::SOAP qw(getServerHandle getClientHandle);
185              
186             # Make sure HoneyClient::Util::Config loads.
187             BEGIN { use_ok('HoneyClient::Util::Config', qw(getVar)) or diag("Can't load HoneyClient::Util::Config package. Check to make sure the package library is correctly listed within the path."); }
188             require_ok('HoneyClient::Util::Config');
189             can_ok('HoneyClient::Util::Config', 'getVar');
190             use HoneyClient::Util::Config qw(getVar);
191              
192             # Suppress all logging messages, since we need clean output for unit testing.
193             Log::Log4perl->init({
194             "log4perl.rootLogger" => "DEBUG, Buffer",
195             "log4perl.appender.Buffer" => "Log::Log4perl::Appender::TestBuffer",
196             "log4perl.appender.Buffer.min_level" => "fatal",
197             "log4perl.appender.Buffer.layout" => "Log::Log4perl::Layout::PatternLayout",
198             "log4perl.appender.Buffer.layout.ConversionPattern" => "%d{yyyy-MM-dd HH:mm:ss} %5p [%M] (%F:%L) - %m%n",
199             });
200              
201             # Make sure SOAP::Lite loads.
202             BEGIN { use_ok('SOAP::Lite') or diag("Can't load SOAP::Lite package. Check to make sure the package library is correctly listed within the path."); }
203             require_ok('SOAP::Lite');
204             use SOAP::Lite;
205              
206             # Make sure SOAP::Transport::HTTP loads.
207             BEGIN { use_ok('SOAP::Transport::HTTP') or diag("Can't load SOAP::Transport::HTTP package. Check to make sure the package library is correctly listed within the path."); }
208             require_ok('SOAP::Transport::HTTP');
209             use SOAP::Transport::HTTP;
210              
211             # Make sure Data::Dumper loads.
212             BEGIN { use_ok('Data::Dumper') or diag("Can't load Data::Dumper package. Check to make sure the package library is correctly listed within the path."); }
213             require_ok('Data::Dumper');
214             use Data::Dumper;
215              
216             =end testing
217              
218             =cut
219              
220             #######################################################################
221              
222             # Include utility access to global configuration.
223 1     1   4352 use HoneyClient::Util::Config qw(getVar);
  0            
  0            
224              
225             # Include the SOAP APIs
226             use SOAP::Lite 0.67;
227              
228             # If you want debugging on, use this line instead.
229             #use SOAP::Lite +trace => 'all';
230             use SOAP::Transport::HTTP;
231              
232             # Include Data Dumper API
233             use Data::Dumper;
234              
235             # Include Logging Library
236             use Log::Log4perl qw(:easy);
237              
238             # The global logging object.
239             our $LOG = get_logger();
240              
241             # Make Dumper format more terse.
242             $Data::Dumper::Terse = 1;
243             $Data::Dumper::Indent = 0;
244              
245             #######################################################################
246             # Private Methods Implemented #
247             #######################################################################
248              
249             # Default handler for any faults that are received by any client.
250             # Inputs: Class, SOAP::SOM
251             # Outputs: None
252             sub _handleFault {
253              
254             # Extract arguments.
255             my ($class, $res) = @_;
256              
257             # Construct error message.
258             # Figure out if the error occurred in transport or
259             # over on the other side.
260             my $errMsg = $class->transport->status; # Assume transport error.
261             if (ref $res) {
262             # Extract base error message.
263             $errMsg = $res->faultcode . ": " . $res->faultstring . "\n";
264              
265             # Extract error details.
266             my $errNo = undef;
267             my $errStr = undef;
268             my $errDetail = "";
269             if (defined($res->faultdetail)) {
270              
271             # Since we don't know what the fault detail may look like,
272             # we output its contents in a generic fashion.
273             # Make Dumper format more terse.
274             $Data::Dumper::Terse = 1;
275             $Data::Dumper::Indent = 0;
276             $errDetail = $res->faultcode . ": " . Dumper($res->faultdetail);
277              
278             }
279              
280             $errMsg = $errMsg . $errDetail . "\n";
281             }
282              
283             $LOG->error("Error occurred during processing. " . $errMsg);
284             die __PACKAGE__ . "->handleFault(): Error occurred during processing.\n" . $errMsg;
285             }
286              
287             #######################################################################
288             # Public Methods Implemented #
289             #######################################################################
290              
291             =pod
292              
293             =head1 EXPORTS
294              
295             =head2 getServerHandle(namespace => $caller, address => $localAddr, port => $localPort)
296              
297             =over 4
298              
299             Returns a new SOAP::Server object, using the caller's package
300             namespace as the dispatch location, if not specified. If neither
301             the $localAddr nor $localPort is specified, then the function will attempt
302             to retrieve the "address" and "port" global configuration variables, set
303             within the caller's namespace.
304              
305             I:
306             B<$caller> is an optional argument, used to explicitly specify the package
307             namespace to be used as the dispatch point.
308             B<$localAddr> is an optional argument, specifying the IP address for the
309             SOAP server to listen on.
310             B<$localPort> is an optional argument, specifying the TCP port for the
311             SOAP server to listen on.
312            
313             I: The corresponding SOAP::Server object if successful, croaks
314             otherwise.
315              
316             =back
317              
318             =begin testing
319              
320             # Check to make sure we can get a valid handle.
321             my $daemon = getServerHandle(namespace => "HoneyClient::Manager::VM");
322             isa_ok($daemon, 'SOAP::Server', "getServerHandle(namespace => 'HoneyClient::Manager::VM')") or diag("The getServerHandle() call failed.");
323              
324             =end testing
325              
326             =cut
327              
328             sub getServerHandle {
329              
330             # Extract arguments.
331             my (%args) = @_;
332             my $argsExist = scalar(%args);
333              
334             # Find out who is calling this function.
335             if (!$argsExist ||
336             !exists($args{'namespace'}) ||
337             !defined($args{'namespace'})) {
338             $args{'namespace'} = caller();
339             }
340              
341             if (!$argsExist ||
342             !exists($args{'address'}) ||
343             !defined($args{'address'})) {
344             $args{'address'} = getVar(name => "address",
345             namespace => $args{'namespace'});
346             }
347              
348             if (!$argsExist ||
349             !exists($args{'port'}) ||
350             !defined($args{'port'})) {
351             $args{'port'} = getVar(name => "port",
352             namespace => $args{'namespace'});
353             }
354              
355             # Log resolved arguments.
356             $LOG->debug(sub {
357             # Make Dumper format more terse.
358             $Data::Dumper::Terse = 1;
359             $Data::Dumper::Indent = 0;
360             Dumper(\%args);
361             });
362              
363             my $daemon = SOAP::Transport::HTTP::Daemon
364             ->new( LocalAddr => $args{'address'},
365             LocalPort => $args{'port'},
366             Reuse => 1 )
367             ->dispatch_to($args{'namespace'})
368             ->options({ compress_threshold => 10000 });
369              
370             # Sanity check.
371             if (!defined($daemon)) {
372             $LOG->fatal("Unable to create SOAP server using namespace " .
373             "'" . $args{'namespace'} . "', listening on " .
374             $args{'address'} . ":" . $args{'port'} . ".");
375             Carp::croak "Error: Unable to create SOAP server using namespace " .
376             "'" . $args{'namespace'} . "', listening on " .
377             $args{'address'} . ":" . $args{'port'} . ".\n";
378             }
379              
380             return $daemon;
381             }
382              
383             =pod
384              
385             =head2 getClientHandle(namespace => $caller, address => $address, port => $port, fault_handler => $faultHandler)
386              
387             =over 4
388              
389             Returns a new SOAP::Lite client object, using the caller's package
390             namespace as the URI, if not specified. If neither
391             the $address nor $port is specified, then the function will attempt
392             to retrieve the "address" and "port" global configuration variables, set
393             within the caller's namespace.
394              
395             I:
396             B<$caller> is an optional argument, used to explicitly specify the package
397             namespace URI.
398             B<$address> is an optional argument, specifying the IP address for the
399             SOAP server to listen on.
400             B<$port> is an optional argument, specifying the TCP port for the
401             SOAP server to listen on.
402             B<$faultHandler> is an optional argument, specifying the code reference to
403             call if a fault occurs during any subsequent SOAP call using this object.
404            
405             I: The corresponding SOAP::Lite object if successful, croaks
406             otherwise.
407              
408             =back
409              
410             =begin testing
411              
412             # Check to make sure we can get a valid handle.
413             my $stub = getClientHandle(namespace => "HoneyClient::Manager::VM");
414             isa_ok($stub, 'SOAP::Lite', "getClientHandle(namespace => 'HoneyClient::Manager::VM')") or diag("The getClientHandle() call failed.");
415              
416             =end testing
417              
418             =cut
419              
420             sub getClientHandle {
421            
422             # Extract arguments.
423             my (%args) = @_;
424             my $argsExist = scalar(%args);
425             #my ($caller, $address, $port, $faultHandler) = @_;
426              
427             # Find out who is calling this function.
428             if (!$argsExist ||
429             !exists($args{'namespace'}) ||
430             !defined($args{'namespace'})) {
431             $args{'namespace'} = caller();
432             }
433              
434             if (!$argsExist ||
435             !exists($args{'address'}) ||
436             !defined($args{'address'})) {
437             $args{'address'} = getVar(name => "address",
438             namespace => $args{'namespace'});
439             }
440              
441             if (!$argsExist ||
442             !exists($args{'port'}) ||
443             !defined($args{'port'})) {
444             $args{'port'} = getVar(name => "port",
445             namespace => $args{'namespace'});
446             }
447            
448             # If no fault handler was supplied, use the default.
449             if (!$argsExist ||
450             !exists($args{'fault_handler'}) ||
451             !defined($args{'fault_handler'})) {
452             $args{'fault_handler'} = \&_handleFault;
453             }
454              
455             # Log resolved arguments.
456             $LOG->debug(sub {
457             # Make Dumper format more terse.
458             $Data::Dumper::Terse = 1;
459             $Data::Dumper::Indent = 0;
460             Dumper(\%args);
461             });
462              
463             my $timeout = getVar(name => "timeout",
464             namespace => $args{'namespace'});
465             my $URL_BASE = "http://" . $args{'address'} . ":" . $args{'port'};
466             my $URL = $URL_BASE . "/" . join('/', split(/::/, $args{'namespace'}));
467              
468             my $stub = SOAP::Lite
469             ->default_ns($URL)
470             ->proxy($URL_BASE, timeout => $timeout);
471              
472             # If we were supplied with a fault handler, register it.
473             if (defined($args{'fault_handler'}) and
474             (ref($args{'fault_handler'}) eq "CODE")) {
475             $stub->on_fault($args{'fault_handler'});
476             }
477            
478             # Sanity check.
479             if (!defined($stub)) {
480             $LOG->fatal("Unable to connect to SOAP server at: " .
481             "$URL");
482             Carp::croak "Error: Unable to connect to SOAP server at: " .
483             "$URL\n";
484             }
485              
486             return $stub;
487             }
488              
489             1;
490              
491             #######################################################################
492             # Additional Module Documentation #
493             #######################################################################
494              
495             __END__