Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / tools / perl-5.8.0 / lib / site_perl / 5.8.0 / sun4-solaris / DBI / ProxyServer.pm
CommitLineData
86530b38
AT
1# -*- perl -*-
2#
3# DBI::ProxyServer - a proxy server for DBI drivers
4#
5# Copyright (c) 1997 Jochen Wiedmann
6#
7# The DBD::Proxy module is free software; you can redistribute it and/or
8# modify it under the same terms as Perl itself. In particular permission
9# is granted to Tim Bunce for distributing this as a part of the DBI.
10#
11#
12# Author: Jochen Wiedmann
13# Am Eisteich 9
14# 72555 Metzingen
15# Germany
16#
17# Email: joe@ispsoft.de
18# Phone: +49 7123 14881
19#
20#
21
22require 5.004;
23use strict;
24
25use RPC::PlServer 0.2001;
26require DBI;
27require Config;
28
29
30package DBI::ProxyServer;
31
32
33my $haveFileSpec = eval { require File::Spec };
34my $tmpDir = $haveFileSpec ? File::Spec->tmpdir() :
35 ($ENV{'TMP'} || $ENV{'TEMP'} || '/tmp');
36my $defaultPidFile = $haveFileSpec ?
37 File::Spec->catdir($tmpDir, "dbiproxy.pid") : "/tmp/dbiproxy.pid";
38
39
40############################################################################
41#
42# Constants
43#
44############################################################################
45
46use vars qw($VERSION @ISA);
47
48$VERSION = "0.2004";
49@ISA = qw(RPC::PlServer DBI);
50
51
52# Most of the options below are set to default values, we note them here
53# just for the sake of documentation.
54my %DEFAULT_SERVER_OPTIONS;
55{
56 my $o = \%DEFAULT_SERVER_OPTIONS;
57 $o->{'chroot'} = undef, # To be used in the initfile,
58 # after loading the required
59 # DBI drivers.
60 $o->{'clients'} =
61 [ { 'mask' => '.*',
62 'accept' => 1,
63 'cipher' => undef
64 }
65 ];
66 $o->{'configfile'} = '/etc/dbiproxy.conf' if -f '/etc/dbiproxy.conf';
67 $o->{'debug'} = 0;
68 $o->{'facility'} = 'daemon';
69 $o->{'group'} = undef;
70 $o->{'localaddr'} = undef; # Bind to any local IP number
71 $o->{'localport'} = undef; # Must set port number on the
72 # command line.
73 $o->{'logfile'} = undef; # Use syslog or EventLog.
74 $o->{'methods'} = {
75 'DBI::ProxyServer' => {
76 'NewHandle' => 1,
77 'CallMethod' => 1,
78 'DestroyHandle' => 1
79 },
80 'DBI::ProxyServer::db' => {
81 'prepare' => 1,
82 'commit' => 1,
83 'rollback' => 1,
84 'STORE' => 1,
85 'FETCH' => 1,
86 'func' => 1,
87 'quote' => 1,
88 'type_info_all' => 1,
89 'table_info' => 1
90 },
91 'DBI::ProxyServer::st' => {
92 'execute' => 1,
93 'STORE' => 1,
94 'FETCH' => 1,
95 'func' => 1,
96 'fetch' => 1,
97 'finish' => 1
98 }
99 };
100 if ($Config::Config{'usethreads'} eq 'define') {
101 $o->{'mode'} = 'threads';
102 } elsif ($Config::Config{'d_fork'} eq 'define') {
103 $o->{'mode'} = 'fork';
104 } else {
105 $o->{'mode'} = 'single';
106 }
107 $o->{'pidfile'} = $defaultPidFile;
108 $o->{'user'} = undef;
109};
110
111
112############################################################################
113#
114# Name: Version
115#
116# Purpose: Return version string
117#
118# Inputs: $class - This class
119#
120# Result: Version string; suitable for printing by "--version"
121#
122############################################################################
123
124sub Version {
125 my $version = $DBI::ProxyServer::VERSION;
126 "DBI::ProxyServer $version, Copyright (C) 1998, Jochen Wiedmann";
127}
128
129
130############################################################################
131#
132# Name: AcceptApplication
133#
134# Purpose: Verify DBI DSN
135#
136# Inputs: $self - This instance
137# $dsn - DBI dsn
138#
139# Returns: TRUE for a valid DSN, FALSE otherwise
140#
141############################################################################
142
143sub AcceptApplication {
144 my $self = shift; my $dsn = shift;
145 $dsn =~ /^dbi:\w+:/i;
146}
147
148
149############################################################################
150#
151# Name: AcceptVersion
152#
153# Purpose: Verify requested DBI version
154#
155# Inputs: $self - Instance
156# $version - DBI version being requested
157#
158# Returns: TRUE for ok, FALSE otherwise
159#
160############################################################################
161
162sub AcceptVersion {
163 my $self = shift; my $version = shift;
164 $DBI::VERSION >= $version;
165}
166
167
168############################################################################
169#
170# Name: AcceptUser
171#
172# Purpose: Verify user and password by connecting to the client and
173# creating a database connection
174#
175# Inputs: $self - Instance
176# $user - User name
177# $password - Password
178#
179############################################################################
180
181sub AcceptUser {
182 my $self = shift; my $user = shift; my $password = shift;
183 return 0 if (!$self->SUPER::AcceptUser($user, $password));
184 my $dsn = $self->{'application'};
185 $self->Debug("Connecting to $dsn as $user");
186 local $ENV{DBI_AUTOPROXY} = ''; # :-)
187 $self->{'dbh'} = eval {
188 DBI::ProxyServer->connect($dsn, $user, $password,
189 { 'PrintError' => 0, 'Warn' => 0,
190 RaiseError => 1 })
191 };
192 if ($@) {
193 $self->Error("Error while connecting to $dsn as $user: $@");
194 return 0;
195 }
196 [1, $self->StoreHandle($self->{'dbh'}) ];
197}
198
199
200sub CallMethod {
201 my $server = shift;
202 my $dbh = $server->{'dbh'};
203 # We could store the private_server attribute permanently in
204 # $dbh. However, we'd have a reference loop in that case and
205 # I would be concerned about garbage collection. :-(
206 $dbh->{'private_server'} = $server;
207 $server->Debug("CallMethod: => " . join(",", @_));
208 my @result = eval { $server->SUPER::CallMethod(@_) };
209 undef $dbh->{'private_server'};
210 if (my $msg = $@) {
211 $server->Error($msg);
212 die $msg;
213 } else {
214 $server->Debug("CallMethod: <= " . join(",", @result));
215 }
216 @result;
217}
218
219
220sub main {
221 my $server = DBI::ProxyServer->new(\%DEFAULT_SERVER_OPTIONS, \@_);
222 $server->Bind();
223}
224
225
226############################################################################
227#
228# The DBI part of the proxyserver is implemented as a DBI subclass.
229# Thus we can reuse some of the DBI methods and overwrite only
230# those that need additional handling.
231#
232############################################################################
233
234DBI::ProxyServer->init_rootclass();
235
236package DBI::ProxyServer::dr;
237
238@DBI::ProxyServer::dr::ISA = qw(DBI::dr);
239
240
241package DBI::ProxyServer::db;
242
243@DBI::ProxyServer::db::ISA = qw(DBI::db);
244
245sub prepare {
246 my($dbh, $statement, $attr, $params) = @_;
247 my $server = $dbh->{'private_server'};
248 if (my $client = $server->{'client'}) {
249 if ($client->{'sql'}) {
250 if ($statement =~ /^\s*(\S+)/) {
251 my $st = $1;
252 if (!($statement = $client->{'sql'}->{$st})) {
253 die "Unknown SQL query: $st";
254 }
255 } else {
256 die "Cannot parse restricted SQL statement: $statement";
257 }
258 }
259 }
260
261 # The difference between the usual prepare and ours is that we implement
262 # a combined prepare/execute. The DBD::Proxy driver doesn't call us for
263 # prepare. Only if an execute happens, then we are called with method
264 # "prepare". Further execute's are called as "execute".
265 my $sth = $dbh->SUPER::prepare($statement, $attr);
266 my @result = $sth->execute($params);
267 my $handle = $server->StoreHandle($sth);
268 my ($NAME, $TYPE);
269 my $NUM_OF_FIELDS = $sth->{NUM_OF_FIELDS};
270 if ($NUM_OF_FIELDS) { # is a SELECT
271 $NAME = $sth->{NAME};
272 $TYPE = $sth->{TYPE};
273 }
274 ($handle, $NUM_OF_FIELDS, $sth->{'NUM_OF_PARAMS'},
275 $NAME, $TYPE, @result);
276}
277
278sub table_info {
279 my $dbh = shift;
280 my $sth = $dbh->SUPER::table_info();
281 my $numFields = $sth->{'NUM_OF_FIELDS'};
282 my $names = $sth->{'NAME'};
283 my $types = $sth->{'TYPE'};
284
285 # We wouldn't need to send all the rows at this point, instead we could
286 # make use of $rsth->fetch() on the client as usual.
287 # The problem is that some drivers (namely DBD::ExampleP, DBD::mysql and
288 # DBD::mSQL) are returning foreign sth's here, thus an instance of
289 # DBI::st and not DBI::ProxyServer::st. We could fix this by permitting
290 # the client to execute method DBI::st, but I don't like this.
291 my @rows;
292 while (my $row = $sth->fetchrow_arrayref()) {
293 push(@rows, [@$row]);
294 }
295 ($numFields, $names, $types, @rows);
296}
297
298
299package DBI::ProxyServer::st;
300
301@DBI::ProxyServer::st::ISA = qw(DBI::st);
302
303sub execute {
304 my $sth = shift; my $params = shift;
305 my @outParams;
306
307 if ($params) {
308 for (my $i = 0; $i < @$params;) {
309 my $param = $params->[$i++];
310 if (!ref($param)) {
311 $sth->bind_param($i, $param);
312 } else {
313 # value, type => bind_param,
314 # value, type, maxlen => bind_param_inout
315 if (@$param <= 2) {
316 $sth->bind_param($i, @$param);
317 } else {
318 $sth->bind_param_inout($i, @$param);
319 my $ref = shift @$param;
320 push(@outParams, $ref);
321 }
322 }
323 }
324 }
325
326 my $rows = $sth->SUPER::execute();
327 ($rows, @outParams);
328}
329
330sub fetch {
331 my $sth = shift; my $numRows = shift || 1;
332 my($ref, @rows);
333 while ($numRows-- && ($ref = $sth->fetchrow_arrayref())) {
334 push(@rows, [@$ref]);
335 }
336 @rows;
337}
338
339
3401;
341
342
343__END__
344
345=head1 NAME
346
347DBI::ProxyServer - a server for the DBD::Proxy driver
348
349
350=head1 SYNOPSIS
351
352 use DBI::ProxyServer;
353 DBI::ProxyServer::main(@ARGV);
354
355
356=head1 DESCRIPTION
357
358DBI::Proxy Server is a module for implementing a proxy for the DBI proxy
359driver, DBD::Proxy. It allows access to databases over the network if the
360DBMS does not offer networked operations. But the proxy server might be
361usefull for you, even if you have a DBMS with integrated network
362functionality: It can be used as a DBI proxy in a firewalled environment.
363
364DBI::ProxyServer runs as a daemon on the machine with the DBMS or on the
365firewall. The client connects to the agent using the DBI driver DBD::Proxy,
366thus in the exactly same way than using DBD::mysql, DBD::mSQL or any other
367DBI driver.
368
369The agent is implemented as a RPC::PlServer application. Thus you have
370access to all the possibilities of this module, in particular encryption
371and a similar configuration file. DBI::ProxyServer adds the possibility of
372query restrictions: You can define a set of queries that a client may
373execute and restrict access to those. (Requires a DBI driver that supports
374parameter binding.) See L</CONFIGURATION FILE>.
375
376
377=head1 OPTIONS
378
379When calling the DBI::ProxyServer::main() function, you supply an
380array of options. (@ARGV, the array of command line options is used,
381if you don't.) These options are parsed by the Getopt::Long module.
382The ProxyServer inherits all of RPC::PlServer's and hence Net::Daemon's
383options and option handling, in particular the ability to read
384options from either the command line or a config file. See
385L<RPC::PlServer(3)>. See L<Net::Daemon(3)>. Available options include
386
387=over 4
388
389=item I<chroot> (B<--chroot=dir>)
390
391(UNIX only) After doing a bind(), change root directory to the given
392directory by doing a chroot(). This is usefull for security, but it
393restricts the environment a lot. For example, you need to load DBI
394drivers in the config file or you have to create hard links to Unix
395sockets, if your drivers are using them. For example, with MySQL, a
396config file might contain the following lines:
397
398 my $rootdir = '/var/dbiproxy';
399 my $unixsockdir = '/tmp';
400 my $unixsockfile = 'mysql.sock';
401 foreach $dir ($rootdir, "$rootdir$unixsockdir") {
402 mkdir 0755, $dir;
403 }
404 link("$unixsockdir/$unixsockfile",
405 "$rootdir$unixsockdir/$unixsockfile");
406 require DBD::mysql;
407
408 {
409 'chroot' => $rootdir,
410 ...
411 }
412
413If you don't know chroot(), think of an FTP server where you can see a
414certain directory tree only after logging in. See also the --group and
415--user options.
416
417=item I<clients>
418
419An array ref with a list of clients. Clients are hash refs, the attributes
420I<accept> (0 for denying access and 1 for permitting) and I<mask>, a Perl
421regular expression for the clients IP number or its host name. See
422L<"Access control"> below.
423
424=item I<configfile> (B<--configfile=file>)
425
426Config files are assumed to return a single hash ref that overrides the
427arguments of the new method. However, command line arguments in turn take
428precedence over the config file. See the L<"CONFIGURATION FILE"> section
429below for details on the config file.
430
431=item I<debug> (B<--debug>)
432
433Turn debugging mode on. Mainly this asserts that logging messages of
434level "debug" are created.
435
436=item I<facility> (B<--facility=mode>)
437
438(UNIX only) Facility to use for L<Sys::Syslog (3)>. The default is
439B<daemon>.
440
441=item I<group> (B<--group=gid>)
442
443After doing a bind(), change the real and effective GID to the given.
444This is usefull, if you want your server to bind to a privileged port
445(<1024), but don't want the server to execute as root. See also
446the --user option.
447
448GID's can be passed as group names or numeric values.
449
450=item I<localaddr> (B<--localaddr=ip>)
451
452By default a daemon is listening to any IP number that a machine
453has. This attribute allows to restrict the server to the given
454IP number.
455
456=item I<localport> (B<--localport=port>)
457
458This attribute sets the port on which the daemon is listening. It
459must be given somehow, as there's no default.
460
461=item I<logfile> (B<--logfile=file>)
462
463Be default logging messages will be written to the syslog (Unix) or
464to the event log (Windows NT). On other operating systems you need to
465specify a log file. The special value "STDERR" forces logging to
466stderr. See L<Net::Daemon::Log(3)> for details.
467
468=item I<mode> (B<--mode=modename>)
469
470The server can run in three different modes, depending on the environment.
471
472If you are running Perl 5.005 and did compile it for threads, then the
473server will create a new thread for each connection. The thread will
474execute the server's Run() method and then terminate. This mode is the
475default, you can force it with "--mode=threads".
476
477If threads are not available, but you have a working fork(), then the
478server will behave similar by creating a new process for each connection.
479This mode will be used automatically in the absence of threads or if
480you use the "--mode=fork" option.
481
482Finally there's a single-connection mode: If the server has accepted a
483connection, he will enter the Run() method. No other connections are
484accepted until the Run() method returns (if the client disconnects).
485This operation mode is usefull if you have neither threads nor fork(),
486for example on the Macintosh. For debugging purposes you can force this
487mode with "--mode=single".
488
489=item I<pidfile> (B<--pidfile=file>)
490
491(UNIX only) If this option is present, a PID file will be created at the
492given location.
493
494=item I<user> (B<--user=uid>)
495
496After doing a bind(), change the real and effective UID to the given.
497This is usefull, if you want your server to bind to a privileged port
498(<1024), but don't want the server to execute as root. See also
499the --group and the --chroot options.
500
501UID's can be passed as group names or numeric values.
502
503=item I<version> (B<--version>)
504
505Supresses startup of the server; instead the version string will
506be printed and the program exits immediately.
507
508=back
509
510
511=head1 CONFIGURATION FILE
512
513The configuration file is just that of I<RPC::PlServer> or I<Net::Daemon>
514with some additional attributes in the client list.
515
516The config file is a Perl script. At the top of the file you may include
517arbitraty Perl source, for example load drivers at the start (usefull
518to enhance performance), prepare a chroot environment and so on.
519
520The important thing is that you finally return a hash ref of option
521name/value pairs. The possible options are listed above.
522
523All possibilities of Net::Daemon and RPC::PlServer apply, in particular
524
525=over 4
526
527=item Host and/or User dependent access control
528
529=item Host and/or User dependent encryption
530
531=item Changing UID and/or GID after binding to the port
532
533=item Running in a chroot() environment
534
535=back
536
537Additionally the server offers you query restrictions. Suggest the
538following client list:
539
540 'clients' => [
541 { 'mask' => '^admin\.company\.com$',
542 'accept' => 1,
543 'users' => [ 'root', 'wwwrun' ],
544 },
545 {
546 'mask' => '^admin\.company\.com$',
547 'accept' => 1,
548 'users' => [ 'root', 'wwwrun' ],
549 'sql' => {
550 'select' => 'SELECT * FROM foo',
551 'insert' => 'INSERT INTO foo VALUES (?, ?, ?)'
552 }
553 }
554
555then only the users root and wwwrun may connect from admin.company.com,
556executing arbitrary queries, but only wwwrun may connect from other
557hosts and is restricted to
558
559 $sth->prepare("select");
560
561or
562
563 $sth->prepare("insert");
564
565which in fact are "SELECT * FROM foo" or "INSERT INTO foo VALUES (?, ?, ?)".
566
567
568
569
570=head1 AUTHOR
571
572 Copyright (c) 1997 Jochen Wiedmann
573 Am Eisteich 9
574 72555 Metzingen
575 Germany
576
577 Email: joe@ispsoft.de
578 Phone: +49 7123 14881
579
580The DBI::ProxyServer module is free software; you can redistribute it
581and/or modify it under the same terms as Perl itself. In particular
582permission is granted to Tim Bunce for distributing this as a part of
583the DBI.
584
585
586=head1 SEE ALSO
587
588L<dbiproxy(1)>, L<DBD::Proxy(3)>, L<DBI(3)>, L<RPC::PlServer(3)>,
589L<RPC::PlClient(3)>, L<Net::Daemon(3)>, L<Net::Daemon::Log(3)>,
590L<Sys::Syslog(3)>, L<Win32::EventLog(3)>, L<syslog(2)>