File Coverage

blib/lib/RPC/pServer.pm
Criterion Covered Total %
statement 138 275 50.1
branch 47 124 37.9
condition 12 35 34.2
subroutine 19 22 86.3
pod 9 12 75.0
total 225 468 48.0


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             #
4             # pRPC - Perl RPC, package for writing simple, RPC like clients and
5             # servers
6             #
7             # RPC::pServer.pm is the module for writing the pRPC server.
8             #
9             #
10             # Copyright (c) 1997 Jochen Wiedmann
11             #
12             # You may distribute under the terms of either the GNU General Public
13             # License or the Artistic License, as specified in the Perl README file.
14             #
15             # Author: Jochen Wiedmann
16             # Am Eisteich 9
17             # 72555 Metzingen
18             # Germany
19             #
20             # Email: wiedmann@neckar-alb.de
21             # Phone: +49 7123 14881
22             #
23             #
24             # $Id: pServer.pm,v 0.1001 1997/09/14 22:53:27 joe Exp $
25             #
26             package RPC::pServer;
27              
28 7     7   11704 use strict;
  7         14  
  7         376  
29 7     7   40 use Carp;
  7         10  
  7         617  
30 7     7   41 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
  7         11  
  7         699  
31 7     7   3962 use RPC::pClient;
  7         19  
  7         582  
32              
33             require Exporter;
34             require DynaLoader;
35              
36             @ISA = qw(Exporter DynaLoader RPC::pClient);
37             # Items to export into callers namespace by default. Note: do not export
38             # names by default without a very good reason. Use EXPORT_OK instead.
39             # Do not simply export all your public functions/methods/constants.
40             @EXPORT = qw(
41            
42             );
43             $VERSION = '0.1005';
44              
45              
46              
47             # Preloaded methods go here.
48              
49 7     7   38 use POSIX();
  7         14  
  7         99  
50 7     7   36 use Sys::Syslog();
  7         12  
  7         152  
51 7     7   33 use IO::Socket();
  7         12  
  7         81  
52 7     7   30 use Socket();
  7         72  
  7         132  
53 7     7   29 use Storable();
  7         20  
  7         19253  
54              
55              
56             ############################################################################
57             #
58             # Name: _ReadConfigFile
59             #
60             # Purpose: Reads a server configuration file
61             #
62             # Inputs: file name
63             #
64             # Returns: a reference to a list of clients, if successfull; the
65             # reference will additionally be stored in the variable
66             # $RPC::pServer::configFile for later use. 'undef'
67             # will be returned in case of errors.
68             #
69             ############################################################################
70              
71             sub Log($$$@) {
72 32     32 0 282 my($self, $level, $msg, @args) = @_;
73 32 50       272 if (!$self->{'stderr'}) {
74 32         186 Sys::Syslog::syslog($level, $msg, @args);
75             } else {
76 0         0 print STDERR "$msg\n";
77             }
78             }
79             my $logClass = "RPC::pServer";
80              
81              
82             sub _ReadConfigFile ($) {
83 0     0   0 my($file) = @_;
84 0         0 my($line, $configFile, $client, $mask, $lineNum);
85              
86 0 0       0 if(defined($RPC::pServer::configFile)) {
87 0         0 return $RPC::pServer::configFile;
88             }
89              
90 0         0 $configFile = [];
91 0 0       0 if (!open(FILE, "<$file")) {
92 0         0 return "Cannot read config file $file: $!";
93             }
94              
95 0         0 $lineNum = 0;
96 0         0 while (defined($line = )) {
97 0         0 ++$lineNum;
98 0         0 $line =~ s/\#.*//; # Comments are allowed
99 0 0       0 if ($line =~ /^\s*accept\s+(\S+)\s*$/i) {
    0          
    0          
    0          
100 0         0 $mask = $1;
101 0         0 $client = { 'mask' => $mask, 'accept' => 1 };
102 0         0 push(@$configFile, $client);
103             } elsif ($line =~ /^\s*deny\s+(\S+)\s*$/i) {
104 0         0 $mask = $1;
105 0         0 $client = { 'mask' => $mask, 'accept' => 0 };
106 0         0 push(@$configFile, $client);
107             } elsif ($line =~ /^\s*(\S+)\s+(.*\S)\s*$/) {
108 0 0       0 if (defined($client)) {
109 0         0 $client->{$1} = $2;
110             } else {
111 0         0 close(FILE);
112 0         0 return "Cannot parse line $lineNum of config file $file.";
113             }
114             } elsif ($line !~ /^\s*$/) {
115 0         0 close(FILE);
116 0         0 return "Cannot parse line $lineNum of config file $file.";
117             }
118             }
119              
120 0         0 close(FILE);
121              
122 0         0 $RPC::pServer::configFile = $configFile;
123             }
124              
125              
126             ############################################################################
127             #
128             # Name: new
129             #
130             # Purpose: Constructor of the RPC::pServer module
131             #
132             # Inputs: Hash list of attributes; see RPC::pServer(3)
133             #
134             # Returns: connection object or 'undef' in case of errors
135             #
136             ############################################################################
137              
138             sub new ($@) {
139 2     2 1 11471 my ($proto) = shift;
140 2   33     135 my ($class) = ref($proto) || $proto;
141 2         66 my ($self) = {@_};
142 2         10 my ($sock);
143              
144 2         41 bless($self, $class);
145              
146             # Read the configuration file, if not already done.
147 2 50       202 if (defined($self->{'configFile'})) {
148 0         0 my ($result) = _ReadConfigFile($self->{'configFile'});
149 0 0       0 if (!ref($result)) {
150 0         0 $self->Log('err', $result);
151 0         0 return $result;
152             }
153 0         0 $self->{'authorizedClients'} = $result;
154             }
155              
156 2 50       44 if (!defined($self->{'inetd'})) {
157             # Non-Inetd-Server
158 2         229 $sock = $self->{'sock'}->accept();
159 2 50       10000490 if (!defined($sock)) {
160 0         0 my $msg = "Cannot accept: $?";
161 0         0 $self->Log('err', $msg);
162 0         0 return $msg;
163             }
164             } else {
165             # Inetd based server; need to work out how to create a
166             # IO::socket object for that
167 0         0 $sock = $self->{'sock'};
168             }
169              
170             #
171             # Check whether the client is authorized to connect
172             #
173 2         48 my ($name, $aliases, $addrtype, $length, @addrs)
174             = gethostbyaddr($sock->peeraddr, &Socket::AF_INET);
175 2         2565 my $client;
176 2         18 foreach $client (@{$self->{'authorizedClients'}}) {
  2         42  
177 0         0 my($alias, $found, $mask);
178 0         0 my (@cfl) = (%$client);
179 0         0 $mask = $client->{'mask'};
180 0         0 $found = 0;
181 0 0 0     0 if ($sock->peerhost =~ /$mask/ ||
182             $name =~ /$mask/) {
183 0         0 $found = 1;
184             }
185 0 0       0 if (!$found) {
186 0         0 foreach $alias (split(/ /, $aliases)) {
187 0 0       0 if ($alias =~ /$mask/) {
188 0         0 $found = 1;
189 0         0 last;
190             }
191             }
192             }
193 0 0       0 if (!$found) {
194 0         0 my $addr;
195 0         0 foreach $addr (@addrs) {
196 0 0       0 if (Socket::inet_ntoa($addr) =~ /$mask/) {
197 0         0 $found = 1;
198 0         0 last;
199             }
200             }
201             }
202 0 0       0 if ($found) {
203 0         0 my ($class, $key);
204 0 0       0 if (!$client->{'accept'}) {
205 0         0 my $msg = sprintf("Access not permitted from %s, %s",
206             $sock->sockhost, $sock->sockport);
207 0         0 $self->Log('err', $msg);
208 0         0 return $msg;
209             }
210 0         0 $self->{'client'} = $client;
211 0 0 0     0 if (defined($key = $client->{'key'}) &&
212             defined($class = $client->{'encryption'})) {
213 0         0 my ($module);
214 0 0       0 if (!defined($module = $client->{'encryptModule'})) {
215 0         0 $module = $class;
216             }
217 0         0 ($self->{'cipher'}) = eval qq{
218             use $module;
219             new $class(pack("H*", \$key));
220             };
221 0         0 $self->Log('debug', "Using encryption: " . $self->{'cipher'});
222              
223 0 0       0 if ($@) {
224 0         0 my $msg = "Cannot create cipher object: $@";
225 0         0 $self->Log('err', $msg);
226 0         0 return $msg;
227             }
228             }
229 0         0 last;
230             }
231             }
232 2 50 33     17 if ($self->{'configFile'} && !$self->{'client'}) {
233 0         0 my $msg = sprintf("Access not permitted from %s, %s",
234             $sock->sockhost, $sock->sockport);
235 0         0 $self->Log('err', $msg);
236 0         0 return $msg;
237             }
238              
239 2         25 $self->Log('notice', sprintf("Accepting connect from %s, port %s",
240             $sock->sockhost, $sock->sockport));
241              
242             #
243             # Ok, the client is allowed to connect. Create Storable
244             # objects and wait for the login message.
245             #
246 2 50       1895 if ($self->_HaveOoStorable) {
247 0         0 $self->{'io'} = Storable->new('file' => *{$sock}{IO},
  0         0  
248             'crypt' => $self->{'cipher'},
249             'netorder' => 1,
250             'forgive_me' => 1);
251 0 0       0 if (!defined($self->{'io'})) {
252 0         0 my $msg = "Cannot create Storable object for read: $!";
253 0         0 $self->Log('err', $msg);
254 0         0 return $msg;
255             }
256             } else {
257 2         20 $self->{'file'} = $sock;
258             }
259              
260 2 50       17 if ($self->{'debug'}) {
261 2         38 $self->Log('debug', "$logClass: Waiting for client to log in.");
262             }
263              
264 2         255 my $loginMsg;
265 2         55 $loginMsg = $self->_Retrieve();
266 2 50       11 if (!defined($loginMsg)) {
267 0         0 my $msg = "Error while logging in: " . $self->error;
268 0         0 $self->Log('err', $msg);
269 0         0 return $msg;
270             }
271 2 50       14 if (ref($loginMsg) ne 'ARRAY') {
272 0         0 my $msg = "Error while logging in: Expected array.";
273 0         0 $self->Log('err', $msg);
274 0         0 return $msg;
275             }
276              
277 2         37 ($self->{'application'}, $self->{'version'}, $self->{'user'},
278             $self->{'password'}) = @$loginMsg;
279 2 50       11 if ($self->{'debug'}) {
280 2   50     37 $self->Log('debug', "$logClass: Client logs in: "
281             . $self->{'application'}
282             . " " . $self->{'version'} . " "
283             . ($self->{'user'} || ''));
284             }
285 2 50 33     369 if (!defined($self->{'application'}) ||
286             !defined($self->{'version'})) {
287 0         0 my $msg = "Protocol error while logging in";
288 0         0 $self->Log('err', $msg);
289 0         0 return $msg;
290             }
291              
292 2         7 $self->{'sock'} = $sock;
293 2         15 $self;
294             }
295              
296              
297             ############################################################################
298             #
299             # Name: Accept, Deny
300             #
301             # Purpose: Methods for accepting or denying a connection
302             #
303             # Inputs: $con - connection object
304             # $msg - Message being sent to the client
305             #
306             # Returns: TRUE for succcess, FALSE otherwise; you might consult
307             # the method $con->error in that case.
308             #
309             ############################################################################
310              
311             sub Accept($$) {
312 2     2 1 19 my ($self, $msg) = @_;
313 2         29 $self->Log('debug', "Accepting client.\n");
314 2         341 $self->_Store([1, $msg]);
315             }
316              
317             sub Deny($$) {
318 0     0 1 0 my ($self, $msg) = @_;
319 0         0 $self->Log('debug', "Denying client.\n");
320 0         0 $self->_Store([0, $msg]);
321             }
322              
323 36     36 0 50 sub error ($) { my $self = shift; $self->{'error'}; }
  36         172  
324              
325              
326             ############################################################################
327             #
328             # Name: Loop
329             #
330             # Purpose: Process client requests
331             #
332             # Inputs: $con - connection object
333             #
334             # Returns: TRUE, if a client request was successfully processed,
335             # FALSE otherwise in which case $con->error is set
336             #
337             ############################################################################
338              
339             sub Loop ($) {
340 24     24 1 22758 my($self) = shift;
341 24         42 my($command, $commandRef);
342 0         0 my(@result);
343              
344 24 50       355 if ($self->{'sock'}->eof()) {
345 0         0 $self->{'error'} = "Cannot talk to Client: EOF";
346 0         0 $self->Log('err', $self->error);
347 0         0 return 0;
348             }
349              
350 24         165 my $msg;
351 24         78 $msg = $self->_Retrieve();
352 24 50       60 if (!defined($msg)) {
353 0         0 $self->{'error'} = "Error while reading client request: "
354             . $self->{'error'};
355 0         0 $self->Log('err', $self->error);
356 0         0 return 0;
357             }
358 24         36 my $ok = 0;
359 24 50       153 if (ref($msg) ne 'ARRAY') {
    50          
    100          
360 0         0 $self->{'error'} = "Error in request data: Expected array.";
361             } elsif (!defined($command = shift @$msg)) {
362 0         0 $self->{'error'} = "Error in request data: Missing command";
363             } elsif (!defined($commandRef = $self->{'funcTable'}->{$command})) {
364 2         9 $self->{'error'} = "Unknown command ($command)";
365             } else {
366 22         29 my($code);
367 22         42 $code = $commandRef->{'code'};
368 22         2216 ($ok, @result) = eval '&$code($self, $commandRef, @$msg)';
369 22 50       1147 if ($@ ne '') {
370 0         0 $ok = 0;
371 0         0 $self->{'error'} = "Function evaluation failed: $@";
372             } else {
373 22 50       54 if (!defined($ok)) {
374 0         0 $ok = 0;
375             }
376 22 100       53 if (!$ok) {
377 2 50       9 if (@result) {
378 2         6 $self->{'error'} = shift @result;
379             } else {
380 0         0 $self->{'error'} = "Unknown error";
381             }
382             } else {
383 20         66 $self->{'error'} = '';
384             }
385             }
386             }
387              
388 24 100       67 if ($self->error) {
    50          
389 4         11 $self->Log('err', "Client Request -> error " . $self->error);
390 4         886 $ok = 0;
391 4         15 @result = ($self->error);
392             } elsif ($self->{'debug'}) {
393 20         76 $self->Log('debug', "$logClass: Client requested $command -> ok");
394             }
395              
396 24 100 100     3253 if (scalar(@result) == 1 && !defined($result[0])) {
397             # If we'd simply use @result now, this would give a warning
398             # "Use of uninitialized value"; even worse, the returned
399             # result would differ from the expected.
400 2 50       11 if (!$self->_Store([$ok, undef])) {
401 0         0 my $error = $self->error;
402 0         0 $self->Log('err', "Error while replying client: $error");
403 0         0 $ok = 0;
404             }
405             } else {
406 22 50       111 if (!$self->_Store([$ok, @result])) {
407 0         0 my $error = $self->error;
408 0         0 $self->Log('err', "Error while replying client: $error");
409 0         0 $ok = 0;
410             }
411             }
412              
413 24         99 return $ok;
414             }
415              
416              
417             ############################################################################
418             #
419             # Name: Encrypt
420             #
421             # Purpose: Get or set the current encryption mode
422             #
423             # Inputs: $self - client object
424             # $crypt - encryption object
425             #
426             # Returns: current encryption object; 'undef' for no encryption
427             #
428             ############################################################################
429              
430             sub Encrypt ($;$) {
431 0     0 1 0 my ($self, $crypt) = @_;
432 0 0       0 if (@_ == 2) {
433 0 0       0 if ($self->_HaveOoStorable) {
434 0         0 $self->{'io'}->{'crypt'} = $crypt;
435             } else {
436 0         0 $self->{'cipher'} = $crypt;
437             }
438             }
439 0 0       0 $self->_HaveOoStorable ? $self->{'io'}->{'crypt'} : $self->{'cipher'};
440             }
441              
442              
443             ############################################################################
444             #
445             # Name: NewHandle, UseHandle, StoreHandle, CallMethod,
446             # DestroyHandle
447             #
448             # Purpose: Support functions for working with objects
449             #
450             # Inputs: $con - server object
451             # $ref - hash reference to the entry in $con's function
452             # table being currently executed; this *must* have
453             # an attribute 'handles' which should be a reference
454             # to a hash array which is part of the server
455             # functions local variables; so you are safe in
456             # a multithreaded environment.
457             # other input, depending on the method
458             #
459             # Returns: All functions guarantee that $con->error is empty in
460             # case of success and nonempty otherwise. StoreHandle
461             # guarantees to return 'undef' for error and a
462             # defined() value for success; so does UseHandle, at
463             # least as long as you don't feed 'undef' objects
464             # into 'StoreHandle'. This is guaranteed by 'NewHandle',
465             # which satisfies the same behaviour. The results of
466             # CallMethod() are unpredictable.
467             #
468             ############################################################################
469              
470             sub UseHandle ($$$) {
471 12     12 1 20 my ($con, $ref, $objectHandle) = @_;
472 12         15 my ($hRef);
473 12 50 33     175 if (!defined($hRef = $ref->{'handles'}) || ref($hRef) ne 'HASH') {
474 0         0 $con->{'error'} = "Mising 'handles' attribute on server";
475 0         0 return;
476             }
477 12 100 33     119 if (!defined($objectHandle) || !exists($hRef->{$objectHandle})) {
478 2         19 $con->{'error'} = "Unknown object handle";
479 2         7 return;
480             }
481 10         26 $con->{'error'} = '';
482 10         24 $hRef->{$objectHandle};
483             }
484              
485             sub DestroyHandle ($$$) {
486 2     2 0 5 my ($con, $ref, $objectHandle) = @_;
487 2         65 my ($hRef);
488 2 50 33     23 if (!defined($hRef = $ref->{'handles'}) || ref($hRef) ne 'HASH') {
489 0         0 $con->{'error'} = "Mising 'handles' attribute on server";
490 0         0 return 0;
491             }
492 2 50       18 if (!exists($hRef->{$objectHandle})) {
493 0         0 $con->{'error'} = "Unknown object handle";
494 0         0 return 0;
495             }
496 2         10 delete $hRef->{$objectHandle};
497 2         9 1;
498             }
499              
500             sub CallMethod ($$@) {
501 12     12 1 94 my ($con, $ref, $objectHandle, $method, @arg) = @_;
502 12         33 my ($objectRef) = UseHandle($con, $ref, $objectHandle);
503 12         20 my (@result);
504              
505 12 100       29 if (!defined($objectRef)) {
506 2         11 $con->{'error'} = "Illegal object handle";
507 2         9 return(0, $con->error);
508             }
509 10 100       24 if ($method eq 'DESTROY') {
510 2 50       80 if (!DestroyHandle($con, $ref, $objectHandle)) {
511 0         0 return (0, $con->error);
512             }
513             } else {
514 8 50       108 if (!$objectRef->can($method)) {
515 0         0 $con->{'error'} = "Unknown method: $method";
516 0         0 return (0, $con->error);
517             }
518 8         447 (@result) = eval '$objectRef->' . $method . '(@arg)';
519 8 50       136 if ($@) {
520 0         0 $con->{'error'} = "Error while executing method: $@";
521 0         0 return (0, $con->error);
522             }
523             }
524 10         27 $con->{'error'} = '';
525 10         153 (1, @result);
526             }
527              
528             sub StoreHandle ($$$) {
529 2     2 1 6 my ($con, $ref, $objectRef) = @_;
530 2         6 my ($hRef);
531 2 50 33     73 if (!defined($hRef = $ref->{'handles'}) || ref($hRef) ne 'HASH') {
532 0         0 $con->{'error'} = "Mising 'handles' attribute on server";
533 0         0 return;
534             }
535 2 50       11 my ($num) = exists($hRef->{'num'}) ? $hRef->{'num'} : 0;
536 2         7 $hRef->{'num'} = ++$num;
537 2         25 $hRef->{$num} = $objectRef;
538 2         21 $con->{'error'} = '';
539 2         9 $num;
540             }
541              
542             sub NewHandle ($$$@) {
543 2     2 1 9 my ($con, $ref, $classWanted, @arg) = @_;
544 2         6 my ($lRef) = $ref->{'classes'};
545              
546             # Check, if access to this class is permitted
547 2         5 my $class;
548 2         20 foreach $class (@$lRef) {
549 2 50       8 if ($class eq $classWanted) {
550             # It is, create the method
551 2         7 my $command = $class . '->new(@arg)';
552 2         166 my ($object) = eval $command;
553 2 50       192 if ($@) {
554 0         0 $con->{'error'} = $@;
555 0         0 return (0, $@);
556             }
557 2 50       8 if (!defined($object)) {
558 0         0 $con->{'error'} = ' Failed to create object, unknown error';
559 0         0 return (0, $con->error);
560             }
561 2         19 my $handle = StoreHandle($con, $ref, $object);
562 2 50 33     9 if ($con->error || !defined($handle)) {
563 0         0 return(0, $con->error);
564             }
565 2         26 return (1, $handle);
566             }
567             }
568 0           $con->{'error'} = "Not permitted to create objects of class $classWanted";
569 0           (0, $con->{'error'});
570             }
571              
572             # Autoload methods go after =cut, and are processed by the autosplit program.
573              
574             1;
575             __END__