.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "RPC::PlServer 3"
.TH RPC::PlServer 3 "2007-06-17" "perl v5.26.3" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
RPC::PlServer \- Perl extension for writing PlRPC servers
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
.Vb 2
\& # Create a subclass of RPC::PlServer
\& use RPC::PlServer;
\&
\& package MyServer;
\& $MyServer::VERSION = \*(Aq0.01\*(Aq;
\& @MyServer::ISA = qw(RPC::PlServer);
\&
\& # Overwrite the Run() method to handle a single connection
\& sub Run {
\& my $self = shift;
\& my $socket = $self\->{\*(Aqsocket\*(Aq};
\& }
\&
\& # Create an instance of the MyServer class
\& package main;
\& my $server = MyServer\->new({\*(Aqlocalport\*(Aq => \*(Aq1234\*(Aq}, \e@ARGV);
\&
\& # Bind the server to its port to make it actually running
\& $server\->Bind();
.Ve
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
PlRPC (Perl \s-1RPC\s0) is a package for implementing servers and clients that
are written in Perl entirely. The name is borrowed from Sun's \s-1RPC\s0
(Remote Procedure Call), but it could as well be \s-1RMI\s0 like Java's "Remote
Method Interface), because PlRPC gives you the complete power of Perl's
\&\s-1OO\s0 framework in a very simple manner.
.PP
RPC::PlServer is the package used on the server side, and you guess what
RPC::PlClient is for. Both share the package RPC::PlServer::Comm for
communication purposes. See \fBPlRPC::Client\fR\|(3) and RPC::PlServer::Comm
for these parts.
.PP
PlRPC works by defining a set of methods that may be executed by the client.
For example, the server might offer a method \*(L"multiply\*(R" to the client. Now
the clients method call
.PP
.Vb 1
\& @result = $client\->multiply($a, $b);
.Ve
.PP
will be immediately mapped to a method call
.PP
.Vb 1
\& @result = $server\->multiply($a, $b);
.Ve
.PP
on the server. The arguments and results will be transferred to or from
the server automagically. (This magic has a name in Perl: It's the
Storable module, my thanks to Raphael Manfredi for this excellent
package.) Simple, eh? :\-)
.PP
The RPC::PlServer and RPC::PlClient are abstract servers and clients: You
have to derive your own classes from it.
.SS "Additional options"
.IX Subsection "Additional options"
The RPC::PlServer inherits all of Net::Daemon's options and attributes
and adds the following:
.IP "\fIcipher\fR" 8
.IX Item "cipher"
The attribute value is an instance of Crypt::DES, Crypt::IDEA or any
other class with the same \s-1API\s0 for block encryption. If you supply
such an attribute, the traffic between client and server will be
encrypted using this option.
.IP "\fImaxmessage\fR (\fB\-\-maxmessage=size\fR)" 8
.IX Item "maxmessage (--maxmessage=size)"
The size of messages exchanged between client and server is restricted,
in order to omit denial of service attacks. By default the limit is
65536 bytes.
.IP "users" 8
.IX Item "users"
This is an attribute of the client object used for Permit/Deny rules
in the config file. It's value is an array ref of user names that
are allowed to connect from the given client. See the example config
file below. \*(L"\s-1CONFIGURATION FILE\*(R"\s0.
.SS "Error Handling"
.IX Subsection "Error Handling"
Error handling is simple with the \s-1RPC\s0 package, because it is based on
Perl exceptions completely. Thus your typical code looks like this:
.PP
.Vb 8
\& eval {
\& # Do something here. Don\*(Aqt care for errors.
\& ...
\& };
\& if ($@) {
\& # An error occurred.
\& ...
\& }
.Ve
.SS "Server Constructors"
.IX Subsection "Server Constructors"
.Vb 1
\& my $server = RPC::PlServer(\e%options, \e@args);
.Ve
.PP
(Class method) This constructor is immediately inherited from the
Net::Daemon package. See \fBNet::Daemon\fR\|(3) for details.
.SS "Access Control"
.IX Subsection "Access Control"
.Vb 3
\& $ok = $self\->AcceptApplication($app);
\& $ok = $self\->AcceptVersion($version);
\& $ok = $self\->AcceptUser($user, $password);
.Ve
.PP
The RPC::PlServer package has a very detailed access control scheme: First
of all it inherits Net::Daemon's host based access control. It adds
version control and user authorization. To achieve that, the method
\&\fIAccept\fR from Net::Daemon is split into three methods,
\&\fIAcceptApplication\fR, \fIAcceptVersion\fR and \fIAcceptUser\fR, each of them
returning \s-1TRUE\s0 or \s-1FALSE.\s0 The client receives the arguments as the attributes
\&\fIapplication\fR, \fIversion\fR, \fIuser\fR and \fIpassword\fR. A client is accepted
only if all of the above methods are returning \s-1TRUE.\s0
.PP
The default implementations are as follows: The AcceptApplication method
returns \s-1TRUE,\s0 if \fB\f(CB$self\fB\fR is a subclass of \fB\f(CB$app\fB\fR. The AcceptVersion
method returns \s-1TRUE,\s0 if the requested version is less or equal to
\&\fB${$class}::VERSION\fR, \f(CW$self\fR being an instance of \fB\f(CB$class\fB\fR. Whether a user
is permitted to connect depends on the client configuration. See
\&\*(L"\s-1CONFIGURATION FILE\*(R"\s0 below for examples.
.SS "Method based access control"
.IX Subsection "Method based access control"
Giving a client the ability to invoke arbitrary methods can be a terrible
security hole. Thus the server has a \fImethods\fR attribute. This is a hash
ref of class names as keys, the values being hash refs again with method
names as the keys. That is, if your hash looks as follows:
.PP
.Vb 11
\& $self\->{\*(Aqmethods\*(Aq} = {
\& \*(AqCalcServer\*(Aq => {
\& \*(AqNewHandle\*(Aq => 1,
\& \*(AqCallMethod\*(Aq => 1 },
\& \*(AqCalculator\*(Aq => {
\& \*(Aqnew\*(Aq => 1,
\& \*(Aqmultiply\*(Aq => 1,
\& \*(Aqadd\*(Aq => 1,
\& \*(Aqdivide\*(Aq => 1,
\& \*(Aqsubtract\*(Aq => 1 }
\& };
.Ve
.PP
then the client may use the CalcServer's \fINewHandle\fR method to create
objects, but only via the permitted constructor Calculator\->new. Once
a Calculator object is created, the server may invoke the methods
multiply, add, divide and subtract.
.SH "CONFIGURATION FILE"
.IX Header "CONFIGURATION FILE"
The server config file is inherited from Net::Daemon. It adds the
\&\fIusers\fR and \fIcipher\fR attribute to the client list. Thus a typical
config file might look as follows:
.PP
.Vb 4
\& # Load external modules; this is not required unless you use
\& # the chroot() option.
\& #require DBD::mysql;
\& #require DBD::CSV;
\&
\& # Create keys
\& my $myhost_key = Crypt::IDEA\->new(\*(Aq83fbd23390ade239\*(Aq);
\& my $bob_key = Crypt::IDEA\->new(\*(Aqbe39893df23f98a2\*(Aq);
\&
\& {
\& # \*(Aqchroot\*(Aq => \*(Aq/var/dbiproxy\*(Aq,
\& \*(Aqfacility\*(Aq => \*(Aqdaemon\*(Aq,
\& \*(Aqpidfile\*(Aq => \*(Aq/var/dbiproxy/dbiproxy.pid\*(Aq,
\& \*(Aquser\*(Aq => \*(Aqnobody\*(Aq,
\& \*(Aqgroup\*(Aq => \*(Aqnobody\*(Aq,
\& \*(Aqlocalport\*(Aq => \*(Aq1003\*(Aq,
\& \*(Aqmode\*(Aq => \*(Aqfork\*(Aq,
\&
\& # Access control
\& \*(Aqclients\*(Aq => [
\& # Accept the local LAN (192.168.1.*)
\& {
\& \*(Aqmask\*(Aq => \*(Aq^192\e.168\e.1\e.\ed+$\*(Aq,
\& \*(Aqaccept\*(Aq => 1,
\& \*(Aqusers\*(Aq => [ \*(Aqbob\*(Aq, \*(Aqjim\*(Aq ],
\& \*(Aqcipher\*(Aq => $myhost_key
\& },
\& # Accept myhost.company.com
\& {
\& \*(Aqmask\*(Aq => \*(Aq^myhost\e.company\e.com$\*(Aq,
\& \*(Aqaccept\*(Aq => 1,
\& \*(Aqusers\*(Aq => [ {
\& \*(Aqname\*(Aq => \*(Aqbob\*(Aq,
\& \*(Aqcipher\*(Aq => $bob_key
\& } ]
\& },
\& # Deny everything else
\& {
\& \*(Aqmask\*(Aq => \*(Aq.*\*(Aq,
\& \*(Aqaccept\*(Aq => 0
\& }
\& ]
\& }
.Ve
.PP
Things you should note: The user list of 192.168.1.* contains scalar
values, but the user list of myhost.company.com contains hash refs:
This is required, because the user configuration is more specific
for user based encryption.
.SH "EXAMPLE"
.IX Header "EXAMPLE"
Enough wasted time, spread the example, not the word. :\-) Let's write
a simple server, say a server for \s-1MD5\s0 digests. The server uses the
external package \s-1MD5,\s0 but the client doesn't need to install the
package. \s-1\fBMD5\s0\fR\|(3). We present the server source here, the client
is part of the RPC::PlClient man page. See \fBRPC::PlClient\fR\|(3).
.PP
.Vb 2
\& #!/usr/bin/perl \-wT
\& # Note the \-T switch! This is always recommended for Perl servers.
\&
\& use strict; # Always a good choice.
\&
\& require RPC::PlServer;
\& require MD5;
\&
\&
\& package MD5_Server; # Clients need to request application
\& # "MD5_Server"
\&
\& $MD5_Server::VERSION = \*(Aq1.0\*(Aq; # Clients will be refused, if they
\& # request version 1.1
\& @MD5_Server::ISA = qw(RPC::PlServer);
\&
\& eval {
\& # Server options below can be overwritten in the config file or
\& # on the command line.
\& my $server = MD5_Server\->new({
\& \*(Aqpidfile\*(Aq => \*(Aq/var/run/md5serv.pid\*(Aq,
\& \*(Aqconfigfile\*(Aq => \*(Aq/etc/md5serv.conf\*(Aq,
\& \*(Aqfacility\*(Aq => \*(Aqdaemon\*(Aq, # Default
\& \*(Aquser\*(Aq => \*(Aqnobody\*(Aq,
\& \*(Aqgroup\*(Aq => \*(Aqnobody\*(Aq,
\& \*(Aqlocalport\*(Aq => 2000,
\& \*(Aqlogfile\*(Aq => 0, # Use syslog
\& \*(Aqmode\*(Aq => \*(Aqfork\*(Aq, # Recommended for Unix
\& \*(Aqmethods\*(Aq => {
\& \*(AqMD5_Server\*(Aq => {
\& \*(AqClientObject\*(Aq => 1,
\& \*(AqCallMethod\*(Aq => 1,
\& \*(AqNewHandle\*(Aq => 1
\& },
\& \*(AqMD5\*(Aq => {
\& \*(Aqnew\*(Aq => 1,
\& \*(Aqadd\*(Aq => 1,
\& \*(Aqhexdigest\*(Aq => 1
\& },
\& }
\& });
\& $server\->Bind();
\& };
.Ve
.SH "SECURITY"
.IX Header "SECURITY"
It has to be said: PlRPC based servers are a potential security problem!
I did my best to avoid security problems, but it is more than likely,
that I missed something. Security was a design goal, but not *the*
design goal. (A well known problem ...)
.PP
I highly recommend the following design principles:
.ie n .SS "Protection against ""trusted"" users"
.el .SS "Protection against ``trusted'' users"
.IX Subsection "Protection against trusted users"
.IP "perlsec" 4
.IX Item "perlsec"
Read the perl security \s-1FAQ\s0 (\f(CW\*(C`perldoc perlsec\*(C'\fR) and use the \f(CW\*(C`\-T\*(C'\fR switch.
.IP "taintperl" 4
.IX Item "taintperl"
\&\fBUse\fR the \f(CW\*(C`\-T\*(C'\fR switch. I mean it!
.IP "Verify data" 4
.IX Item "Verify data"
Never untaint strings withouth verification, better verify twice.
For example the \fICallMethod\fR function first checks, whether an
object handle is valid before coercing a method on it.
.IP "Be restrictive" 4
.IX Item "Be restrictive"
Think twice, before you give a client access to a method.
.IP "perlsec" 4
.IX Item "perlsec"
And just in case I forgot it: Read the \f(CW\*(C`perlsec\*(C'\fR man page. :\-)
.SS "Protection against untrusted users"
.IX Subsection "Protection against untrusted users"
.IP "Host based authorization" 4
.IX Item "Host based authorization"
PlRPC has a builtin host based authorization scheme; use it!
See \*(L"\s-1CONFIGURATION FILE\*(R"\s0.
.IP "User based authorization" 4
.IX Item "User based authorization"
PlRPC has a builtin user based authorization scheme; use it!
See \*(L"\s-1CONFIGURATION FILE\*(R"\s0.
.IP "Encryption" 4
.IX Item "Encryption"
Using encryption with PlRPC is extremely easy. There is absolutely
no reason for communicating unencrypted with the clients. Even
more: I recommend two phase encryption: The first phase is the
login phase, where to use a host based key. As soon as the user
has authorized, you should switch to a user based key. See the
DBI::ProxyServer for an example.
.SH "AUTHOR AND COPYRIGHT"
.IX Header "AUTHOR AND COPYRIGHT"
The PlRPC-modules are
.PP
.Vb 2
\& Copyright (C) 1998, Jochen Wiedmann
\& Email: jochen.wiedmann at freenet.de
\&
\& All rights reserved.
.Ve
.PP
You may distribute this package under the terms of either the \s-1GNU\s0
General Public License or the Artistic License, as specified in the
Perl \s-1README\s0 file.
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\fBRPC::PlClient\fR\|(3), \fBRPC::PlServer::Comm\fR\|(3), \fBNet::Daemon\fR\|(3),
\&\fBNet::Daemon::Log\fR\|(3), \fBStorable\fR\|(3), \fBSys::Syslog\fR\|(3),
\&\fBWin32::EventLog\fR\|(3)
.PP
See \fBDBI::ProxyServer\fR\|(3) for an example application.