# -*- perl -*- # # # PlRPC - Perl RPC, package for writing simple, RPC like clients and # servers # # # Copyright (c) 1997,1998 Jochen Wiedmann # # You may distribute under the terms of either the GNU General Public # License or the Artistic License, as specified in the Perl README file. # # Author: Jochen Wiedmann # Email: jochen.wiedmann at freenet.de # require 5.004; use strict; require Storable; package RPC::PlServer::Comm; $RPC::PlServer::Comm::VERSION = '0.1003'; ############################################################################ # # Name: new (Class method) # # Purpose: Constructor # # Inputs: $class - This class # $attr - Hash ref of attributes # # Result: Server object for success, error message otherwise # ############################################################################ sub new ($) { my($class, $attr) = @_; my $self = {}; bless($self, (ref($class) || $class)); if (my $comp = $attr->{'compression'}) { if ($comp eq 'off') { $self->{'compression'} = undef; } elsif ($comp eq 'gzip') { require Compress::Zlib; $self->{'compression'} = 'gzip'; } else { die "Unknown compression type ($comp), use 'off' or 'gzip'"; } } if (my $cipher = $attr->{'cipher'}) { $self->{'cipher'} = $cipher; } if (my $maxmessage = $attr->{'maxmessage'}) { $self->{'maxmessage'} = $maxmessage; } $self; } ############################################################################ # # Name: Write # # Purpose: Writing to a PlRPC socket; used by both the client (when # sending a method name and arguments) and the server (for # sending the result list). Communication occurrs in packets. # Each packet is preceeded by 4 bytes with the true packet # size. If encryption happens, then the packet is padded with # NUL bytes to a multiple of blocksize bytes. However, the # stored size remains unchanged. # # Inputs: $self - Instance of RPC::PlServer or RPC::PlClient # $socket - The socket to write to # $args - Reference to array of arguments being sent # # Result: Nothing; dies in case of errors. # ############################################################################ sub Write ($$$) { my($self, $socket, $msg) = @_; my $encodedMsg = Storable::nfreeze($msg); $encodedMsg = Compress::Zlib::compress($encodedMsg) if ($self->{'compression'}); my($encodedSize) = length($encodedMsg); if (my $cipher = $self->{'cipher'}) { my $size = $cipher->blocksize; if (my $addSize = length($encodedMsg) % $size) { $encodedMsg .= chr(0) x ($size - $addSize); } $msg = ''; for (my $i = 0; $i < length($encodedMsg); $i += $size) { $msg .= $cipher->encrypt(substr($encodedMsg, $i, $size)); } $encodedMsg = $msg; } local $\; if (!$socket->print(pack("N", $encodedSize), $encodedMsg) || !$socket->flush()) { die "Error while writing socket: $!"; } } ############################################################################ # # Name: Read # # Purpose: Reading from a PlRPC socket; used by both the client (when # receiving a result list) and the server (for receiving the # method name and arguments). Counterpart of Write, see # above for specs. # # Inputs: $self - Instance of RPC::PlServer or RPC::PlClient # $socket - The socket to read from # # Result: Array ref to result list; dies in case of errors. # ############################################################################ sub Read($$) { my($self, $socket) = @_; my $result; my($encodedSize, $readSize, $blockSize); $readSize = 4; $encodedSize = ''; while ($readSize > 0) { my $result = $socket->read($encodedSize, $readSize, length($encodedSize)); if (!$result) { return undef if defined($result); die "Error while reading socket: $!"; } $readSize -= $result; } $encodedSize = unpack("N", $encodedSize); my $max = $self->getMaxMessage(); die "Maximum message size of $max exceeded, use option 'maxmessage' to" . " increase" if $max && $encodedSize > $max; $readSize = $encodedSize; if ($self->{'cipher'}) { $blockSize = $self->{'cipher'}->blocksize; if (my $addSize = ($encodedSize % $blockSize)) { $readSize += ($blockSize - $addSize); } } my $msg = ''; my $rs = $readSize; while ($rs > 0) { my $result = $socket->read($msg, $rs, length($msg)); if (!$result) { die "Unexpected EOF" if defined $result; die "Error while reading socket: $!"; } $rs -= $result; } if ($self->{'cipher'}) { my $cipher = $self->{'cipher'}; my $encodedMsg = $msg; $msg = ''; for (my $i = 0; $i < $readSize; $i += $blockSize) { $msg .= $cipher->decrypt(substr($encodedMsg, $i, $blockSize)); } $msg = substr($msg, 0, $encodedSize); } $msg = Compress::Zlib::uncompress($msg) if ($self->{'compression'}); Storable::thaw($msg); } ############################################################################ # # Name: Init # # Purpose: Initialize an object for using RPC::PlServer::Comm methods # # Input: $self - Instance # # Returns: The instance in case of success, dies in case of trouble. # ############################################################################ ############################################################################ # # Name: getMaxMessage # # Purpose: Returns the maximum size of a message # # Inputs: None # # Returns: Maximum message size or 65536, if none specified # ############################################################################ sub getMaxMessage() { my $self = shift; return defined($self->{'maxmessage'}) ? $self->{'maxmessage'} : 65536; } 1;