File Coverage

blib/lib/Net/Server.pm
Criterion Covered Total %
statement 417 724 57.6
branch 197 456 43.2
condition 78 210 37.1
subroutine 67 104 64.4
pod 41 74 55.4
total 800 1568 51.0


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             # Net::Server
4             # ABSTRACT: Extensible Perl internet server
5             #
6             # Copyright (C) 2001-2022
7             #
8             # Paul Seamons
9             #
10             # Rob Brown
11             #
12             # This package may be distributed under the terms of either the
13             # GNU General Public License
14             # or the
15             # Perl Artistic License
16             #
17             # All rights reserved.
18             #
19             ################################################################
20              
21             package Net::Server;
22              
23 28     28   54835 use strict;
  28         77  
  28         1052  
24 28     28   171 use Socket qw(AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM);
  28         439  
  28         12013  
25 28     28   252 use IO::Socket ();
  28         58  
  28         438  
26 28     28   1274 use IO::Select ();
  28         4253  
  28         583  
27 28     28   10796 use POSIX ();
  28         148026  
  28         856  
28 28     28   1486 use Net::Server::Proto ();
  28         43  
  28         553  
29 28         249388 use Net::Server::Daemonize qw(check_pid_file create_pid_file safe_fork
30 28     28   11227 get_uid get_gid set_uid set_gid);
  28         58  
31              
32             our $VERSION = '2.014';
33              
34             sub new {
35 71   50 71 1 1173 my $class = shift || die "Missing class";
36 71 100       730 my $args = @_ == 1 ? shift : {@_};
37 71         542 return bless {server => {%$args}}, $class;
38             }
39              
40 62     62 0 96 sub net_server_type { __PACKAGE__ }
41 0     0 0 0 sub get_property { $_[0]->{'server'}->{$_[1]} }
42 0     0 0 0 sub set_property { $_[0]->{'server'}->{$_[1]} = $_[2] }
43              
44             sub run {
45 70 100   70 1 33531 my $self = ref($_[0]) ? shift() : shift->new; # pass package or object
46 70 100       1180 $self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_];
  3         11  
47 70         511 $self->_initialize; # configure all parameters
48              
49 70         257 $self->post_configure; # verification of passed parameters
50 70         264 $self->post_configure_hook; # user customizable hook
51              
52 70         248 $self->pre_bind; # finalize ports to be bound
53 70         375 $self->bind; # connect to port(s), setup selection handle for multi port
54 70         1054 $self->post_bind_hook; # user customizable hook
55 70         196 $self->post_bind; # allow for chrooting, becoming a different user and group
56              
57 70         275 $self->pre_loop_hook; # user customizable hook
58 70         285 $self->loop; # repeat accept/process cycle
59              
60 59         177 $self->server_close; # close the server and release the port
61             }
62              
63             sub run_client_connection {
64 13     13 1 34 my $self = shift;
65 13         29 my $c = $self->{'server'}->{'client'};
66              
67 13         118 $self->post_accept($c); # prepare client for processing
68 13         118 $self->get_client_info($c); # determines information about peer and local
69 13         127 $self->post_accept_hook($c); # user customizable hook
70              
71 13   33     81 my $ok = $self->allow_deny($c) && $self->allow_deny_hook($c); # do allow/deny check on client info
72 13 50       40 if ($ok) {
73 13         58 $self->process_request($c); # This is where the core functionality of a Net::Server should be.
74             } else {
75 0         0 $self->request_denied_hook($c); # user customizable hook
76             }
77              
78 6         148 $self->post_process_request_hook($ok); # user customizable hook
79 6         34 $self->post_process_request; # clean up client connection, etc
80 6         224 $self->post_client_connection_hook; # one last hook
81             }
82              
83             ###----------------------------------------------------------------###
84              
85             sub _initialize {
86 73     73   189 my $self = shift;
87 73   50     420 my $prop = $self->{'server'} ||= {};
88              
89 73 100       243 $self->commandline($self->_get_commandline) if ! eval { local $SIG{__DIE__}; $self->commandline }; # save for a HUP
  73         636  
  73         477  
90 73         429 $self->configure_hook; # user customizable hook
91 73         419 $self->configure; # allow for reading of commandline, program, and configuration file parameters
92              
93 73 50       168 my @defaults = %{ $self->default_values || {} }; # allow yet another way to pass defaults
  73         162  
94 73 100       340 $self->process_args(\@defaults) if @defaults;
95             }
96              
97             sub commandline {
98 143     143 0 224 my $self = shift;
99 143 50       793 $self->{'server'}->{'commandline'} = ref($_[0]) ? shift : \@_ if @_;
    100          
100 143   100     1638 return $self->{'server'}->{'commandline'} || die "commandline was not set during initialization";
101             }
102              
103             sub _get_commandline {
104 70     70   278 my $self = shift;
105 70         284 my $script = $0;
106 70 50 33     1128 $script = $ENV{'PWD'} .'/'. $script if $script =~ m|^[^/]+/| && $ENV{'PWD'}; # add absolute to relative - avoid Cwd
107 70         292 $script =~ /^(.+)$/; # untaint for later use in hup
108 70         667 return [$1, @ARGV]
109             }
110              
111       73 1   sub configure_hook {}
112              
113             sub configure {
114 100     100 1 149 my $self = shift;
115 100         155 my $prop = $self->{'server'};
116 100 100 66     628 my $template = ($_[0] && ref($_[0])) ? shift : undef;
117              
118 100 100       230 $self->process_args(\@ARGV, $template) if @ARGV; # command line
119 100 50       846 $self->process_args($prop->{'_run_args'}, $template) if $prop->{'_run_args'}; # passed to run
120              
121 100 100       378 if ($prop->{'conf_file'}) {
122 8         20 $self->process_args($self->_read_conf($prop->{'conf_file'}), $template);
123             } else {
124 92   50     256 my $def = $self->default_values || {};
125 92 100       387 $self->process_args($self->_read_conf($def->{'conf_file'}), $template) if $def->{'conf_file'};
126             }
127             }
128              
129 133     133 1 339 sub default_values { {} }
130              
131             sub post_configure {
132 70     70 1 95 my $self = shift;
133 70         94 my $prop = $self->{'server'};
134              
135 70 50 33     209 $prop->{'log_level'} = 2 if ! defined($prop->{'log_level'}) || $prop->{'log_level'} !~ /^\d+$/;
136 70 50       134 $prop->{'log_level'} = 4 if $prop->{'log_level'} > 4;
137 70         324 $self->initialize_logging;
138              
139 70 50       133 if ($prop->{'pid_file'}) { # see if a daemon is already running
140 0 0       0 if (! eval{ check_pid_file($prop->{'pid_file'}) }) {
  0         0  
141 0 0       0 warn $@ if !$ENV{'BOUND_SOCKETS'};
142 0         0 $self->fatal(my $e = $@);
143             }
144             }
145              
146 70 100       214 if (! $prop->{'_is_inet'}) { # completetely daemonize by closing STDIN, STDOUT (should be done before fork)
147 69 50 33     350 if ($prop->{'setsid'} || length($prop->{'log_file'})) {
148 0 0       0 open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
149 0 0       0 open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
150             }
151             }
152              
153 70 50       146 if (!$ENV{'BOUND_SOCKETS'}) { # don't need to redo this if hup'ing
154 70 50 33     322 if ($prop->{'setsid'} || $prop->{'background'}) {
155 0         0 my $pid = eval { safe_fork() };
  0         0  
156 0 0       0 $self->fatal(my $e = $@) if ! defined $pid;
157 0 0       0 exit(0) if $pid;
158 0         0 $self->log(2, "Process Backgrounded");
159             }
160              
161 70 50       193 POSIX::setsid() if $prop->{'setsid'}; # completely remove myself from parent process
162             }
163              
164 70 50 33     321 if (length($prop->{'log_file'})
    50          
165             && !$prop->{'log_function'}) {
166 0         0 open STDERR, '>&_SERVER_LOG' || die "Cannot open STDERR to _SERVER_LOG [$!]";
167             } elsif ($prop->{'setsid'}) { # completely daemonize by closing STDERR (should be done after fork)
168 0         0 open STDERR, '>&STDOUT' || die "Cannot open STDERR to STDOUT [$!]";
169             }
170              
171             # allow for a pid file (must be done after backgrounding and chrooting)
172             # Remove of this pid may fail after a chroot to another location... however it doesn't interfere either.
173 70 50       122 if ($prop->{'pid_file'}) {
174 0 0       0 if (eval { create_pid_file($prop->{'pid_file'}) }) {
  0         0  
175 0         0 $prop->{'pid_file_unlink'} = 1;
176             } else {
177 0         0 $self->fatal(my $e = $@);
178             }
179             }
180              
181             # make sure that allow and deny look like array refs
182 70         232 $prop->{$_} = [] for grep {! ref $prop->{$_}} qw(allow deny cidr_allow cidr_deny);
  280         649  
183              
184 70 50 0     155 $prop->{'reverse_lookups'} ||= 1 if $prop->{'double_reverse_lookups'};
185             $prop->{'double_reverse_lookups'} = $1 || $prop->{'double_reverse_lookups'} || 1
186 70 50 0     169 if $prop->{'reverse_lookups'} && $prop->{'reverse_lookups'} =~ /^(?:double|2)(.*)$/i;
      33        
187             }
188              
189             sub initialize_logging {
190 70     70 0 103 my $self = shift;
191 70         162 my $prop = $self->{'server'};
192 70 50       161 if (! defined($prop->{'log_file'})) {
193 70         122 $prop->{'log_file'} = ''; # log to STDERR
194 70         126 return;
195             }
196              
197             # pluggable logging
198 0 0       0 if (my $code = $prop->{'log_function'}) {
    0          
199 0 0       0 if (ref $code ne 'CODE') {
200 0         0 require Scalar::Util;
201 0 0       0 die "Passed log_function $code was not a valid method of server, or was not a code object\n" if ! $self->can($code);
202 0         0 my $copy = $self;
203 0     0   0 $prop->{'log_function'} = sub { $copy->$code(@_) };
  0         0  
204 0         0 Scalar::Util::weaken($copy);
205             }
206             } elsif ($prop->{'log_file'} =~ /^([a-zA-Z]\w*(?:::[a-zA-Z]\w*)*)$/) {
207 0         0 my $pkg = "Net::Server::Log::$prop->{'log_file'}";
208 0         0 (my $file = "$pkg.pm") =~ s|::|/|g;
209 0 0 0     0 if (eval { require $file }) {
  0 0       0  
210 0         0 $prop->{'log_function'} = $pkg->initialize($self);
211 0         0 $prop->{'log_class'} = $pkg;
212 0         0 return;
213 0         0 } elsif ($file =~ /::/ || grep {-e "$_/$file"} @INC) {
214 0         0 $self->fatal("Unable to load log module $pkg from file $file: $@");
215             }
216             }
217              
218             # regular file based logging
219 0 0       0 die "Unsecure filename \"$prop->{'log_file'}\"" if $prop->{'log_file'} !~ m|^([\:\w\.\-/\\]+)$|;
220 0         0 $prop->{'log_file'} = $1; # open a logging file
221 0 0       0 open(_SERVER_LOG, ">>", $prop->{'log_file'})
222             || die "Couldn't open log file \"$prop->{'log_file'}\" [$!].";
223 0         0 _SERVER_LOG->autoflush(1);
224 0         0 push @{ $prop->{'chown_files'} }, $prop->{'log_file'};
  0         0  
225             }
226              
227       70 1   sub post_configure_hook {}
228              
229 69     69   366 sub _server_type { ref($_[0]) }
230              
231             sub pre_bind { # make sure we have good port parameters
232 69     69 1 102 my $self = shift;
233 69         96 my $prop = $self->{'server'};
234              
235 69         191 my $super = $self->net_server_type;
236 69         244 my $type = $self->_server_type;
237 69 100       414 if ($self->isa('Net::Server::MultiType')) {
238 3   33     62 my $base = delete($prop->{'_recursive_multitype'}) || Net::Server::MultiType->net_server_type;
239 3         12 $super = "$super -> MultiType -> $base";
240             }
241 69 50       280 $type .= " (type $super)" if $type ne $super;
242 69         352 $self->log(2, $self->log_time ." $type starting! pid($$)");
243              
244 69         194 $prop->{'sock'} = [grep {$_} map { $self->proto_object($_) } @{ $self->prepared_ports }];
  100         277  
  100         295  
  69         339  
245 69 50       101 $self->fatal("No valid socket parameters found") if ! @{ $prop->{'sock'} };
  69         198  
246             }
247              
248             sub prepared_ports {
249 69     69 0 110 my $self = shift;
250 69         113 my $prop = $self->{'server'};
251              
252 69         167 my ($ports, $hosts, $protos, $ipvs) = @$prop{qw(port host proto ipv)};
253 69   33     161 $ports ||= $prop->{'ports'};
254 69 100 66     447 if (!defined($ports) || (ref($ports) && !@$ports)) {
      66        
255 7         18 $ports = $self->default_port;
256 7 50 33     26 if (!defined($ports) || (ref($ports) && !@$ports)) {
      33        
257 0         0 $ports = default_port();
258 0         0 $self->log(2, "Port Not Defined. Defaulting to '$ports'");
259             }
260             }
261              
262 69         95 my %bound;
263 69         220 my $bind = $prop->{'_bind'} = [];
264 69 100       263 for my $_port (ref($ports) ? @$ports : $ports) {
265 100 100       334 my $_host = ref($hosts) ? $hosts->[ @$bind >= @$hosts ? -1 : $#$bind + 1] : $hosts; # if ports are greater than hosts - augment with the last host
    50          
266 100 100       307 my $_proto = ref($protos) ? $protos->[@$bind >= @$protos ? -1 : $#$bind + 1] : $protos;
    50          
267 100 100       239 my $_ipv = ref($ipvs) ? $ipvs->[ @$bind >= @$ipvs ? -1 : $#$bind + 1] : $ipvs;
    50          
268 100         318 foreach my $info ($self->port_info($_port, $_host, $_proto, $_ipv)) {
269 100         250 my ($port, $host, $proto, $ipv) = @$info{qw(port host proto ipv)}; # use cleaned values
270 100 50 33     555 if ($port ne "0" && $bound{"$host\e$port\e$proto\e$ipv"}++) {
271 0         0 $self->log(2, "Duplicate configuration (\U$proto\E) on [$host]:$port with IPv$ipv) - skipping");
272 0         0 next;
273             }
274 100         238 push @$bind, $info;
275             }
276             }
277              
278 69         189 return $bind;
279             }
280              
281             sub port_info {
282 100     100 0 208 my ($self, $port, $host, $proto, $ipv) = @_;
283 100         754 return Net::Server::Proto->parse_info($port, $host, $proto, $ipv, $self);
284             }
285              
286             sub proto_object {
287 100     100 0 162 my ($self, $info) = @_;
288 100         407 return Net::Server::Proto->object($info, $self);
289             }
290              
291             sub bind { # bind to the port (This should serve all but INET)
292 12     12 1 26 my $self = shift;
293 12         54 my $prop = $self->{'server'};
294              
295 12 50       48 if (exists $ENV{'BOUND_SOCKETS'}) {
296 0         0 $self->restart_open_hook;
297 0         0 $self->log(2, "Binding open file descriptors");
298 0         0 my %map;
299 0         0 foreach my $info (split /\s*;\s*/, $ENV{'BOUND_SOCKETS'}) {
300 0         0 my ($fd, $host, $port, $proto, $ipv, $orig) = split /\|/, $info;
301 0 0       0 $orig = $port if ! defined $orig; # allow for things like service ports or port 0
302 0 0       0 $fd = ($fd =~ /^(\d+)$/) ? $1 : $self->fatal("Bad file descriptor");
303 0         0 $map{"$host|$orig|$proto|$ipv"}->{$fd} = $port;
304             }
305 0         0 foreach my $sock (@{ $prop->{'sock'} }) {
  0         0  
306 0         0 $sock->log_connect($self);
307 0 0       0 if (my $ref = $map{$sock->hup_string}) {
308 0         0 my ($fd, $port) = each %$ref;
309 0         0 $sock->reconnect($fd, $self, $port);
310 0         0 delete $ref->{$fd};
311 0 0       0 delete $map{$sock->hup_string} if ! keys %$ref;
312             } else {
313 0         0 $self->log(2, "Added new port configuration");
314 0         0 $sock->connect($self);
315             }
316             }
317 0         0 foreach my $str (keys %map) {
318 0         0 foreach my $fd (keys %{ $map{$str} }) {
  0         0  
319 0         0 $self->log(2, "Closing un-mapped port ($str) on fd $fd");
320 0         0 POSIX::close($fd);
321             }
322             }
323 0         0 delete $ENV{'BOUND_SOCKETS'};
324 0         0 $self->{'hup_waitpid'} = 1;
325              
326             } else { # connect to fresh ports
327 12         31 foreach my $sock (@{ $prop->{'sock'} }) {
  12         94  
328 18         109 $sock->log_connect($self);
329 18         69 $sock->connect($self);
330             }
331             }
332              
333 12 100 100     34 if (@{ $prop->{'sock'} } > 1 || $prop->{'multi_port'}) {
  12         121  
334 7         42 $prop->{'multi_port'} = 1;
335 7         192 $prop->{'select'} = IO::Select->new; # if more than one socket we'll need to select on it
336 7         171 $prop->{'select'}->add($_) for @{ $prop->{'sock'} };
  7         63  
337             } else {
338 5         24 $prop->{'multi_port'} = undef;
339 5         56 $prop->{'select'} = undef;
340             }
341             }
342              
343       70 1   sub post_bind_hook {}
344              
345              
346             sub post_bind { # secure the process and background it
347 13     13 1 33 my $self = shift;
348 13         32 my $prop = $self->{'server'};
349              
350 13 50       49 if (! defined $prop->{'group'}) {
    0          
351 13         380 $self->log(1, "Group Not Defined. Defaulting to EGID '$)'");
352 13         109 $prop->{'group'} = $);
353             } elsif ($prop->{'group'} =~ /^([\w.-]+(?:[ ,][\w.-]+)*)$/) {
354 0         0 $prop->{'group'} = eval { get_gid($1) };
  0         0  
355 0 0       0 $self->fatal(my $e = $@) if $@;
356             } else {
357 0         0 $self->fatal("Invalid group \"$prop->{'group'}\"");
358             }
359              
360 13 50       76 if (! defined $prop->{'user'}) {
    0          
361 13         209 $self->log(1, "User Not Defined. Defaulting to EUID '$>'");
362 13         82 $prop->{'user'} = $>;
363             } elsif ($prop->{'user'} =~ /^([\w.-]+)$/) {
364 0         0 $prop->{'user'} = eval { get_uid($1) };
  0         0  
365 0 0       0 $self->fatal(my $e = $@) if $@;
366             } else {
367 0         0 $self->fatal("Invalid user \"$prop->{'user'}\"");
368             }
369              
370             # chown any files or sockets that we need to
371 13 50 33     301 if ($prop->{'group'} ne $) || $prop->{'user'} ne $>) {
372 0         0 my @chown_files;
373 0         0 push @chown_files, map {$_->NS_port} grep {$_->NS_proto =~ /^UNIX/} @{ $prop->{'sock'} };
  0         0  
  0         0  
  0         0  
374 0 0       0 push @chown_files, $prop->{'pid_file'} if $prop->{'pid_file_unlink'};
375 0 0       0 push @chown_files, $prop->{'lock_file'} if $prop->{'lock_file_unlink'};
376 0 0       0 push @chown_files, @{ $prop->{'chown_files'} || [] };
  0         0  
377 0         0 my $uid = $prop->{'user'};
378 0         0 my $gid = (split /\ /, $prop->{'group'})[0];
379 0         0 foreach my $file (@chown_files){
380 0 0       0 chown($uid, $gid, $file) || $self->fatal("Couldn't chown \"$file\" [$!]");
381             }
382             }
383              
384 13 50       55 if ($prop->{'chroot'}) {
385 0 0       0 $self->fatal("Specified chroot \"$prop->{'chroot'}\" doesn't exist.") if ! -d $prop->{'chroot'};
386 0         0 $self->log(2, "Chrooting to $prop->{'chroot'}");
387 0 0       0 chroot($prop->{'chroot'}) || $self->fatal("Couldn't chroot to \"$prop->{'chroot'}\": $!");
388             }
389              
390             # drop privileges
391 13         40 eval {
392 13 50       111 if ($prop->{'group'} ne $)) {
393 0         0 $self->log(2, "Setting gid to \"$prop->{'group'}\"");
394 0         0 set_gid($prop->{'group'} );
395             }
396 13 50       163 if ($prop->{'user'} ne $>) {
397 0         0 $self->log(2, "Setting uid to \"$prop->{'user'}\"");
398 0         0 set_uid($prop->{'user'});
399             }
400             };
401 13 50       121 if ($@) {
402 0 0       0 if ($> == 0) {
    0          
403 0         0 $self->fatal(my $e = $@);
404             } elsif ($< == 0) {
405 0         0 $self->log(2, "NOTICE: Effective UID changed, but Real UID is 0: $@");
406             } else {
407 0         0 $self->log(2, my $e = $@);
408             }
409             }
410              
411 13         117 $prop->{'requests'} = 0; # record number of request
412              
413 13     0   597 $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->server_close; };
  0         0  
414 13         221 $SIG{'PIPE'} = 'IGNORE'; # most cases, a closed pipe will take care of itself
415 13         183 $SIG{'CHLD'} = \&sig_chld; # catch children (mainly for Fork and PreFork but works for any chld)
416 13     0   262 $SIG{'HUP'} = sub { $self->sig_hup };
  0         0  
417             }
418              
419             sub sig_chld {
420 0     0 0 0 1 while waitpid(-1, POSIX::WNOHANG()) > 0;
421 0         0 $SIG{'CHLD'} = \&sig_chld;
422             }
423              
424       70 1   sub pre_loop_hook {}
425              
426             sub loop {
427 9     9 1 19 my $self = shift;
428 9         108 while ($self->accept) {
429 12         178 $self->run_client_connection;
430 5 100       59 last if $self->done;
431             }
432             }
433              
434             sub accept {
435 13     13 0 983 my $self = shift;
436 13         28 my $prop = $self->{'server'};
437              
438 13         27 my $sock = undef;
439 13         23 my $retries = 30;
440 13         68 while ($retries--) {
441 13 100       38 if ($prop->{'multi_port'}) { # with more than one port, use select to get the next one
442 8 50       81 return 0 if $prop->{'_HUP'};
443 8   50     64 $sock = $self->accept_multi_port || next; # keep trying for the rest of retries
444 7 50       32 return 0 if $prop->{'_HUP'};
445 7 50       51 if ($self->can_read_hook($sock)) {
446 0         0 $retries++;
447 0         0 next;
448             }
449             } else {
450 5         10 $sock = $prop->{'sock'}->[0]; # single port is bound - just accept
451             }
452 12 50       134 $self->fatal("Received a bad sock!") if ! defined $sock;
453              
454 12 100       225 if (SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(), Socket::SO_TYPE())) { # receive a udp packet
455 1         37 $prop->{'client'} = $sock;
456 1         3 $prop->{'udp_true'} = 1;
457 1         5 $prop->{'udp_peer'} = $sock->recv($prop->{'udp_data'}, $sock->NS_recv_len, $sock->NS_recv_flags);
458              
459             } else { # blocking accept per proto
460 11         331 delete $prop->{'udp_true'};
461 11         65 $prop->{'client'} = $sock->accept();
462             }
463              
464 12 50       158 return 0 if $prop->{'_HUP'};
465 12 50       172 return 1 if $prop->{'client'};
466              
467 0         0 $self->log(2,"Accept failed with $retries tries left: $!");
468 0         0 sleep(1);
469             }
470              
471 0         0 $self->log(1,"Ran out of accept retries!");
472 0         0 return undef;
473             }
474              
475              
476             sub accept_multi_port {
477 8     8 0 104 my @waiting = shift->{'server'}->{'select'}->can_read();
478 7 50       4050 return undef if ! @waiting;
479 7         140 return $waiting[rand @waiting];
480             }
481              
482       7 1   sub can_read_hook {}
483              
484             sub post_accept {
485 12     12 1 27 my $self = shift;
486 12         27 my $prop = $self->{'server'};
487 12   33     37 my $client = shift || $prop->{'client'};
488              
489 12         22 $prop->{'requests'}++;
490 12 100       44 return if $prop->{'udp_true'}; # no need to do STDIN/STDOUT in UDP
491              
492 11 50       129 if (!$client) {
493 0         0 $self->log(1,"Client socket information could not be determined!");
494 0         0 return;
495             }
496              
497 11 100       198 $client->post_accept() if $client->can("post_accept");
498 11 100       4944 if (! $prop->{'no_client_stdout'}) {
499 10         178 close STDIN; # duplicate some handles and flush them
500 10         153 close STDOUT;
501 10 100 66     176 if ($prop->{'tie_client_stdout'} || ($client->can('tie_stdout') && $client->tie_stdout)) {
    50 100        
502 4 50       108 open STDIN, '<', '/dev/null' or die "Couldn't open STDIN to the client socket: $!";
503 4 50       100 open STDOUT, '>', '/dev/null' or die "Couldn't open STDOUT to the client socket: $!";
504 4 50       52 tie *STDOUT, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdout_callback'} or die "Couldn't tie STDOUT: $!";
505 4 50       18 tie *STDIN, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdin_callback'} or die "Couldn't tie STDIN: $!";
506             } elsif (defined(my $fileno = fileno $prop->{'client'})) {
507 6 50       131 open STDIN, '<&', $fileno or die "Couldn't open STDIN to the client socket: $!";
508 6 50       119 open STDOUT, '>&', $fileno or die "Couldn't open STDOUT to the client socket: $!";
509             } else {
510 0         0 *STDIN = \*{ $client };
  0         0  
511 0         0 *STDOUT = \*{ $client };
  0         0  
512             }
513 10         68 STDIN->autoflush(1);
514 10         438 STDOUT->autoflush(1);
515 10         249 select STDOUT;
516             }
517             }
518              
519             sub get_client_info {
520 12     12 1 23 my $self = shift;
521 12         25 my $prop = $self->{'server'};
522 12   33     46 my $client = shift || $prop->{'client'};
523              
524 12 50       38 if ($client->NS_proto =~ /^UNIX/) {
525 0         0 delete @$prop{qw(sockaddr sockport peeraddr peerport peerhost peerhost_rev)};
526 0 0 0     0 $self->log(3, $self->log_time." CONNECT ".$client->NS_proto." Socket: \"".$client->NS_port."\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'};
527 0         0 return;
528             }
529              
530 12 50       155 if (my $sockname = $client->sockname) {
531 12         335 $prop->{'sockaddr'} = $client->sockhost;
532 12         714 $prop->{'sockport'} = $client->sockport;
533             } else {
534 0   0     0 @{ $prop }{qw(sockaddr sockhost sockport)} = ($ENV{'REMOTE_HOST'} || '0.0.0.0', 'inet.test', 0); # commandline
  0         0  
535             }
536              
537 12         287 my $addr;
538 12 100       163 if ($prop->{'udp_true'}) {
    50          
539 1 50       20 if ($client->sockdomain == AF_INET) {
540 1         16 ($prop->{'peerport'}, $addr) = Socket::sockaddr_in($prop->{'udp_peer'});
541 1         10 $prop->{'peeraddr'} = Socket::inet_ntoa($addr);
542             } else {
543 0         0 ($prop->{'peerport'}, $addr) = Socket6::sockaddr_in6($prop->{'udp_peer'});
544 0 0       0 $prop->{'peeraddr'} = Socket6->can('inet_ntop')
545             ? Socket6::inet_ntop($client->sockdomain, $addr)
546             : Socket::inet_ntoa($addr);
547             }
548             } elsif ($prop->{'peername'} = $client->peername) {
549 11         304 $addr = $client->peeraddr;
550 11         380 $prop->{'peeraddr'} = $client->peerhost;
551 11         375 $prop->{'peerport'} = $client->peerport;
552             } else {
553 0         0 @{ $prop }{qw(peeraddr peerhost peerport)} = ('0.0.0.0', 'inet.test', 0); # commandline
  0         0  
554             }
555              
556 12         222 delete @$prop{qw(peerhost peerhost_rev)};
557 12 50 33     144 if ($addr && $prop->{'reverse_lookups'}) {
558 0 0 0     0 if ($client->can('peerhostname')) {
    0          
559 0         0 $prop->{'peerhost'} = $client->peerhostname;
560             } elsif ($INC{'Socket6.pm'} && Socket6->can('getnameinfo')) {
561 0         0 my @res = Socket6::getnameinfo($client->peername, 0);
562 0 0       0 $prop->{'peerhost'} = $res[0] if @res > 1;
563             } else {
564 0         0 $prop->{'peerhost'} = gethostbyaddr($addr, AF_INET);
565             }
566 0 0 0     0 if ($prop->{'peerhost'} && $prop->{'double_reverse_lookups'}) {
567 0         0 $prop->{'peerhost_rev'} = {map {$_->[0] => 1} Net::Server::Proto->get_addr_info($prop->{'peerhost'})};
  0         0  
568             }
569             }
570              
571             $self->log(3, $self->log_time
572             ." CONNECT ".$client->NS_proto
573             ." Peer: \"[$prop->{'peeraddr'}]:$prop->{'peerport'}\"".($prop->{'peerhost'} ? " ($prop->{'peerhost'}) " : '')
574 12 0 33     133 ." Local: \"[$prop->{'sockaddr'}]:$prop->{'sockport'}\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'};
    50          
575             }
576              
577       13 1   sub post_accept_hook {}
578              
579             sub allow_deny {
580 13     13 1 30 my $self = shift;
581 13         22 my $prop = $self->{'server'};
582 13   66     47 my $sock = shift || $prop->{'client'};
583              
584             # unix sockets are immune to this check
585 13 50 33     130 return 1 if $sock && $sock->NS_proto =~ /^UNIX/;
586              
587             # work around Net::CIDR::cidrlookup() croaking,
588             # if first parameter is an IPv4 address in IPv6 notation.
589 13 50       66 my $peeraddr = ($prop->{'peeraddr'} =~ /^\s*::ffff:(\d+(?:\.\d+){3})$/) ? $1 : $prop->{'peeraddr'};
590              
591 13 50       43 if ($prop->{'double_reverse_lookups'}) {
592 0 0       0 return 0 if ! $self->double_reverse_lookup($peeraddr, $prop->{'peerhost'}, $prop->{'peerhost_rev'}, $prop->{'peeraddr'})
593             }
594              
595             # if no allow or deny parameters are set, allow all
596 13         109 return 1 if ! @{ $prop->{'allow'} }
597 13         84 && ! @{ $prop->{'deny'} }
598 13         56 && ! @{ $prop->{'cidr_allow'} }
599 13 50 33     85 && ! @{ $prop->{'cidr_deny'} };
  13   33     191  
      33        
600              
601             # if the addr or host matches a deny, reject it immediately
602 0         0 foreach (@{ $prop->{'deny'} }) {
  0         0  
603             return 0 if $prop->{'reverse_lookups'}
604 0 0 0     0 && defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/;
      0        
605 0 0       0 return 0 if $peeraddr =~ /^$_$/;
606             }
607 0 0       0 if (@{ $prop->{'cidr_deny'} }) {
  0         0  
608 0         0 require Net::CIDR;
609 0 0       0 return 0 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_deny'} });
  0         0  
610             }
611              
612             # if the addr or host isn't blocked yet, allow it if it is allowed
613 0         0 foreach (@{ $prop->{'allow'} }) {
  0         0  
614             return 1 if $prop->{'reverse_lookups'}
615 0 0 0     0 && defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/;
      0        
616 0 0       0 return 1 if $peeraddr =~ /^$_$/;
617             }
618 0 0       0 if (@{ $prop->{'cidr_allow'} }) {
  0         0  
619 0         0 require Net::CIDR;
620 0 0       0 return 1 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_allow'} });
  0         0  
621             }
622              
623 0         0 return 0;
624             }
625              
626             sub double_reverse_lookup {
627 0     0 1 0 my ($self, $addr, $host, $rev_addrs, $orig_addr) = @_;
628 0   0     0 my $cfg = $self->{'server'}->{'double_reverse_lookups'} || '';
629 0 0       0 if (! $host) {
    0          
630 0         0 $self->log(3, $self->log_time ." Double reverse missing host from addr $addr");
631 0         0 return 0;
632             } elsif (! $rev_addrs) {
633 0         0 $self->log(3, $self->log_time ." Double reverse missing reverse addrs from host $host ($addr)");
634 0         0 return 0;
635             }
636 0 0 0     0 my $extra = ($orig_addr && $orig_addr ne $addr) ? ", orig_addr: $orig_addr" : '';
637 0 0 0     0 if (! $rev_addrs->{$addr} && ! $rev_addrs->{$orig_addr}) {
    0          
    0          
638 0 0       0 $self->log(3, $self->log_time ." Double reverse did not match: addr: $addr, host: $host"
639             .($cfg =~ /detail/i ? ", addrs: (".join(' ', sort keys %$rev_addrs).")$extra" : ''));
640 0         0 return 0;
641             } elsif ($cfg =~ /autofail/i) {
642 0         0 $self->log(3, $self->log_time ." Double reverse autofail: addr: $addr, host: $host, addrs: (".join(' ', sort keys %$rev_addrs).")$extra");
643 0         0 return 0;
644             } elsif ($cfg =~ /debug/) {
645 0         0 $self->log(3, $self->log_time ." Double reverse debug: addr: $addr, host: $host, addrs: (".join(' ', sort keys %$rev_addrs).")$extra");
646             }
647 0         0 return 1;
648             }
649              
650 13     13 1 40 sub allow_deny_hook { 1 } # false to deny request
651              
652       0 1   sub request_denied_hook {}
653              
654             sub process_request { # sample echo server - override for full functionality
655 10     10 1 34 my $self = shift;
656 10         72 my $prop = $self->{'server'};
657              
658 10 100       47 if ($prop->{'udp_true'}) { # udp echo server
659 1   33     3 my $client = shift || $prop->{'client'};
660 1 50       4 if ($prop->{'udp_data'} =~ /dump/) {
661 0         0 require Data::Dumper;
662 0         0 return $client->send(Data::Dumper::Dumper($self), 0);
663             }
664 1         9 return $client->send("You said \"$prop->{'udp_data'}\"", 0);
665             }
666              
667 9         707 print "Welcome to \"".ref($self)."\" ($$)\015\012";
668 9         2137 my $previous_alarm = alarm 30;
669 9         51 eval {
670 9     0   278 local $SIG{'ALRM'} = sub { die "Timed Out!\n" };
  0         0  
671 9         1239 while () {
672 9         1533 s/[\r\n]+$//;
673 9         342 print ref($self),":$$: You said \"$_\"\015\012";
674 9         352 $self->log(5, $_); # very verbose log
675 9 50       126 if (/get\s+(\w+)/) { print "$1: $self->{'server'}->{$1}\015\012" }
  0 50       0  
    100          
    50          
676 0         0 elsif (/dump/) { require Data::Dumper; print Data::Dumper::Dumper($self) }
  0         0  
677 3         7 elsif (/quit/) { last }
678 6         113 elsif (/exit/) { $self->server_close }
679 0         0 alarm 30; # another 30
680             }
681 3         55 alarm($previous_alarm);
682             };
683 3         15 alarm 0;
684 3 50       15 print "Timed Out.\015\012" if $@ eq "Timed Out!\n";
685             }
686              
687       6 1   sub post_process_request_hook {}
688              
689       6 1   sub post_client_connection_hook {}
690              
691             sub post_process_request {
692 6     6 1 22 my $self = shift;
693 6         82 $self->close_client_stdout;
694             }
695              
696             sub close_client_stdout {
697 6     6 0 14 my $self = shift;
698 6         12 my $prop = $self->{'server'};
699 6 100       21 return if $prop->{'udp_true'};
700              
701 5 100       18 if (! $prop->{'no_client_stdout'}) {
702 4 100       11 my $t = tied *STDOUT; if ($t) { undef $t; untie *STDOUT };
  4         12  
  2         4  
  2         42  
703 4 100       12 $t = tied *STDIN; if ($t) { undef $t; untie *STDIN };
  4         11  
  2         6  
  2         6  
704 4 50       173 open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
705 4 50       146 open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
706             }
707 5         84 $prop->{'client'}->close;
708             }
709              
710             sub done {
711 3     3 0 9 my $self = shift;
712 3 50       9 $self->{'server'}->{'done'} = shift if @_;
713 3         17 return $self->{'server'}->{'done'};
714             }
715              
716       5 1   sub pre_fork_hook {}
717       4 1   sub register_child {}
718       1 1   sub child_init_hook {}
719       1 1   sub child_finish_hook {}
720              
721             sub run_dequeue { # fork off a child process to handle dequeuing
722 0     0 0 0 my $self = shift;
723 0         0 $self->pre_fork_hook('dequeue');
724 0         0 my $pid = fork;
725 0 0       0 $self->fatal("Bad fork [$!]") if ! defined $pid;
726 0 0       0 if (!$pid) { # child
727             $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = $SIG{'HUP'} = sub {
728 0     0   0 $self->child_finish_hook('dequeue');
729 0         0 exit;
730 0         0 };
731 0         0 $SIG{'PIPE'} = $SIG{'TTIN'} = $SIG{'TTOU'} = 'DEFAULT';
732 0         0 $self->child_init_hook('dequeue');
733 0         0 $self->dequeue();
734 0         0 $self->child_finish_hook('dequeue');
735 0         0 exit;
736             }
737 0         0 $self->log(4, "Running dequeue child $pid");
738              
739             $self->{'server'}->{'children'}->{$pid}->{'status'} = 'dequeue'
740 0 0       0 if $self->{'server'}->{'children'};
741 0         0 $self->register_child($pid, 'dequeue');
742             }
743              
744 11     11 0 44623 sub default_port { 20203 }
745              
746       0 0   sub dequeue {}
747              
748       12 1   sub pre_server_close_hook {}
749              
750             sub server_close {
751 12     12 1 59 my ($self, $exit_val) = @_;
752 12         42 my $prop = $self->{'server'};
753              
754 12         214 $SIG{'INT'} = 'DEFAULT';
755              
756             ### if this is a child process, signal the parent and close
757             ### normally the child shouldn't, but if they do...
758             ### otherwise the parent continues with the shutdown
759             ### this is safe for nonstandard forked child processes
760             ### as they will not have server_close as a handler
761 12 50 66     167 if (defined($prop->{'ppid'})
      33        
762             && $prop->{'ppid'} != $$
763             && ! defined($prop->{'no_close_by_child'})) {
764 0         0 $self->close_parent;
765 0         0 exit;
766             }
767              
768 12         128 $self->pre_server_close_hook;
769              
770 12         75 $self->log(2, $self->log_time . " Server closing!");
771              
772 12 0 33     108 if ($prop->{'kind_quit'} && $prop->{'children'}) {
773 0         0 $self->log(3, "Attempting a slow shutdown");
774 0         0 $prop->{$_} = 0 for qw(min_servers max_servers);
775 0         0 $self->hup_children; # send children signal to finish up
776 0         0 while (1) {
777 0         0 Net::Server::SIG::check_sigs();
778 0 0       0 $self->coordinate_children if $self->can('coordinate_children');
779 0 0       0 last if !keys %{$self->{'server'}->{'children'}};
  0         0  
780 0         0 sleep 1;
781             }
782             }
783              
784 12 50 33     160 if ($prop->{'_HUP'} && $prop->{'leave_children_open_on_hup'}) {
785 0         0 $self->hup_children;
786              
787             } else {
788 12 100       98 $self->close_children() if $prop->{'children'};
789 12         270 $self->post_child_cleanup_hook;
790             }
791              
792 12 50 66     127 if (defined($prop->{'lock_file'})
      66        
793             && -e $prop->{'lock_file'}
794             && defined($prop->{'lock_file_unlink'})) {
795 2 50       96 unlink($prop->{'lock_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'lock_file'}\" [$!]");
796             }
797 12 0 33     51 if (defined($prop->{'pid_file'})
      33        
      0        
798             && -e $prop->{'pid_file'}
799             && !$prop->{'_HUP'}
800             && defined($prop->{'pid_file_unlink'})) {
801 0 0       0 unlink($prop->{'pid_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'pid_file'}\" [$!]");
802             }
803 12 50       39 if (defined($prop->{'sem'})) {
804 0         0 $prop->{'sem'}->remove();
805             }
806              
807 12 50       43 if ($prop->{'_HUP'}) {
808 0         0 $self->restart_close_hook();
809 0         0 $self->hup_server; # execs at the end
810             }
811              
812 12         106 $self->shutdown_sockets;
813 12 50       36 return $self if $prop->{'no_exit_on_close'};
814 12         116 $self->server_exit($exit_val);
815             }
816              
817             sub server_exit {
818 12     12 1 35 my ($self, $exit_val) = @_;
819 12   50     3318 exit($exit_val || 0);
820             }
821              
822             sub shutdown_sockets {
823 12     12 1 23 my $self = shift;
824 12         32 my $prop = $self->{'server'};
825              
826 12         46 foreach my $sock (@{ $prop->{'sock'} }) { # unlink remaining socket files (if any)
  12         93  
827 16         293 $sock->shutdown(2);
828 16 50       441 unlink $sock->NS_port if $sock->NS_proto =~ /^UNIX/;
829             }
830              
831 12         98 $prop->{'sock'} = []; # delete the sock objects
832 12         64 return 1;
833             }
834              
835             ### Allow children to send INT signal to parent (or use another method)
836             ### This method is only used by forking servers
837             sub close_parent {
838 0     0 0 0 my $self = shift;
839 0         0 my $prop = $self->{'server'};
840 0 0       0 die "Missing parent pid (ppid)" if ! $prop->{'ppid'};
841 0         0 kill 'INT', $prop->{'ppid'};
842             }
843              
844             ### SIG INT the children
845             ### This method is only used by forking servers (ie Fork, PreFork)
846             sub close_children {
847 3     3 0 7 my $self = shift;
848 3         7 my $prop = $self->{'server'};
849 3 50 50     80 return unless $prop->{'children'} && scalar keys %{ $prop->{'children'} };
  3         17  
850              
851 3         6 foreach my $pid (keys %{ $prop->{'children'} }) {
  3         32  
852 4         20 $self->log(4, "Kill TERM pid $pid");
853 4 50 33     147 if (kill('TERM', $pid) || ! kill(0, $pid)) { # if it is killable, kill it
854 4         55 $self->delete_child($pid);
855             }
856             }
857              
858 3         37 1 while waitpid(-1, POSIX::WNOHANG()) > 0;
859             }
860              
861              
862 0     0 0 0 sub is_prefork { 0 }
863              
864             sub hup_children {
865 0     0 0 0 my $self = shift;
866 0         0 my $prop = $self->{'server'};
867 0 0 0     0 return unless defined $prop->{'children'} && scalar keys %{ $prop->{'children'} };
  0         0  
868 0 0       0 return if ! $self->is_prefork;
869 0         0 $self->log(2, "Sending children hup signal");
870              
871 0         0 for my $pid (keys %{ $prop->{'children'} }) {
  0         0  
872 0         0 $self->log(4, "Kill HUP pid $pid");
873 0 0       0 kill('HUP', $pid) or $self->log(2, "Failed to kill pid $pid: $!");
874             }
875             }
876              
877       12 1   sub post_child_cleanup_hook {}
878              
879             ### handle sig hup
880             ### this will prepare the server for a restart via exec
881             sub sig_hup {
882 0     0 0 0 my $self = shift;
883 0         0 my $prop = $self->{'server'};
884              
885 0         0 $self->log(2, "Received a SIG HUP");
886              
887 0         0 my $i = 0;
888 0         0 my @fd;
889 0         0 $prop->{'_HUP'} = [];
890 0         0 foreach my $sock (@{ $prop->{'sock'} }) {
  0         0  
891 0   0     0 my $fd = POSIX::dup($sock->fileno) || $self->fatal("Cannot duplicate the socket [$!]");
892              
893             # hold on to the socket copy until exec;
894             # just temporary: any socket domain will do,
895             # forked process will decide to use IO::Socket::IP or IO::Socket::INET6 if necessary
896 0         0 $prop->{'_HUP'}->[$i] = IO::Socket::INET->new;
897 0 0       0 $prop->{'_HUP'}->[$i]->fdopen($fd, 'w') || $self->fatal("Cannot open to file descriptor [$!]");
898              
899             # turn off the FD_CLOEXEC bit to allow reuse on exec
900 0         0 require Fcntl;
901 0         0 $prop->{'_HUP'}->[$i]->fcntl(Fcntl::F_SETFD(), my $flags = "");
902              
903 0         0 push @fd, $fd .'|'. $sock->hup_string; # save file-descriptor and host|port|proto|ipv
904              
905 0         0 $sock->close();
906 0         0 $i++;
907             }
908 0         0 delete $prop->{'select'}; # remove any blocking obstacle
909 0         0 $ENV{'BOUND_SOCKETS'} = join "; ", @fd;
910              
911 0 0 0     0 if ($prop->{'leave_children_open_on_hup'} && scalar keys %{ $prop->{'children'} }) {
  0         0  
912 0         0 $ENV{'HUP_CHILDREN'} = join "\n", map {"$_\t$prop->{'children'}->{$_}->{'status'}"} sort keys %{ $prop->{'children'} };
  0         0  
  0         0  
913             }
914             }
915              
916              
917             sub hup_server {
918 0     0 0 0 my $self = shift;
919 0         0 $self->log(0, $self->log_time()." Re-exec server during HUP");
920 0         0 delete @ENV{$self->hup_delete_env_keys};
921 0         0 exec @{ $self->commandline };
  0         0  
922             }
923              
924 0     0 0 0 sub hup_delete_env_keys { return qw(PATH) }
925              
926       0 1   sub restart_open_hook {} # this hook occurs if a server has been HUP'ed it occurs just before opening to the fileno's
927              
928       0 1   sub restart_close_hook {} # this hook occurs if a server has been HUP'ed it occurs just before exec'ing the server
929              
930             ###----------------------------------------------------------###
931              
932             sub fatal {
933 0     0 0 0 my ($self, $error) = @_;
934 0         0 my ($package, $file, $line) = caller;
935 0         0 $self->fatal_hook($error, $package, $file, $line);
936 0         0 $self->log(0, $self->log_time ." $error\n at line $line in file $file");
937 0         0 $self->server_close(1);
938             }
939              
940       0 1   sub fatal_hook {}
941              
942             ###----------------------------------------------------------###
943              
944             sub log {
945 93     93 1 270 my ($self, $level, $msg, @therest) = @_;
946 93         150 my $prop = $self->{'server'};
947 93 50       273 return if ! $prop->{'log_level'};
948 93 100 66     906 return if $level =~ /^\d+$/ && $level > $prop->{'log_level'};
949 68 50       158 $msg = sprintf($msg, @therest) if @therest; # if multiple arguments are passed, assume that the first is a format string
950              
951 68 100       151 if ($prop->{'log_function'}) {
952 5 50       7 return if eval { $prop->{'log_function'}->($level, $msg); 1 };
  5         14  
  5         30  
953 0         0 my $err = $@;
954 0 0 0     0 if ($prop->{'log_class'} && $prop->{'log_class'}->can('handle_error')) {
955 0         0 $prop->{'log_class'}->handle_log_error($self, $err, [$level, $msg]);
956             } else {
957 0         0 $self->handle_log_error($err, [$level, $msg]);
958             }
959             }
960              
961 63 50       233 return if $level !~ /^\d+$/;
962 63         246 $self->write_to_log_hook($level, $msg);
963             }
964              
965              
966 0     0 0 0 sub handle_log_error { my ($self, $error) = @_; die $error }
  0         0  
967 0     0 1 0 sub handle_syslog_error { &handle_log_error }
968              
969             sub write_to_log_hook {
970 63     63 1 116 my ($self, $level, $msg) = @_;
971 63         94 my $prop = $self->{'server'};
972 63         106 chomp $msg;
973 63         149 $msg =~ s/([^\n\ -\~])/sprintf("%%%02X",ord($1))/eg;
  0         0  
974              
975 63 50       200 if ($prop->{'log_file'}) {
    50          
976 0         0 print _SERVER_LOG $msg, "\n";
977             } elsif ($prop->{'setsid'}) {
978             # do nothing ?
979             } else {
980 63         198 my $old = select STDERR;
981 63         162 print $msg. "\n";
982 63         310 select $old;
983             }
984             }
985              
986              
987             sub log_time {
988 81     81 0 3232 my ($sec,$min,$hour,$day,$mon,$year) = localtime;
989 81         1338 return sprintf "%04d/%02d/%02d-%02d:%02d:%02d", $year + 1900, $mon + 1, $day, $hour, $min, $sec;
990             }
991              
992             ###----------------------------------------------------------###
993              
994             sub options {
995 106     106 0 311 my $self = shift;
996 106   50     221 my $ref = shift || {};
997 106         159 my $prop = $self->{'server'};
998              
999 106         237 foreach (qw(port host proto ipv allow deny cidr_allow cidr_deny)) {
1000 848 100       1321 if (! defined $prop->{$_}) {
    100          
1001 552         1164 $prop->{$_} = [];
1002             } elsif (! ref $prop->{$_}) {
1003 8         13 $prop->{$_} = [$prop->{$_}]; # nicely turn us into an arrayref if we aren't one already
1004             }
1005 848         1297 $ref->{$_} = $prop->{$_};
1006             }
1007              
1008 106         209 foreach (qw(conf_file
1009             user group chroot log_level
1010             log_file log_function pid_file background setsid
1011             listen ipv6_package reverse_lookups double_reverse_lookups
1012             no_close_by_child
1013             no_client_stdout tie_client_stdout tied_stdout_callback tied_stdin_callback
1014             leave_children_open_on_hup
1015             )) {
1016 2120         4173 $ref->{$_} = \$prop->{$_};
1017             }
1018 106         173 return $ref;
1019             }
1020              
1021              
1022             ### routine for parsing commandline, module, and conf file
1023             ### method has the benefit of leaving unused arguments in @ARGV
1024             sub process_args {
1025 133     133 0 271 my ($self, $args, $template) = @_;
1026 133 100 66     835 $self->options($template = {}) if ! $template || ! ref $template;
1027 133 0 66     1042 if (!$_[2] && !scalar(keys %$template) && !$self->{'server'}->{'_no_options'}++) {
      33        
1028 0         0 warn "Configuration options were empty - skipping any commandline, config file, or run argument parsing.\n";
1029             }
1030              
1031             # we want subsequent calls to not overwrite or add to previously set values so that command line arguments win
1032 133         184 my %previously_set;
1033 133         453 foreach (my $i = 0; $i < @$args; $i++) {
1034 336 100 100     2227 if ($args->[$i] =~ /^(?:--)?(\w+)(?:[=\ ](\S+))?$/
1035             && exists $template->{$1}) {
1036 318         851 my ($key, $val) = ($1, $2);
1037 318         439 splice @$args, $i, 1;
1038 318 100       592 if (! defined $val) {
1039 315 50 66     1283 if ($i > $#$args
      33        
1040             || ($args->[$i] && $args->[$i] =~ /^--\w+/)) {
1041 0         0 $val = 1; # allow for options such as --setsid
1042             } else {
1043 315         466 $val = splice @$args, $i, 1;
1044 315 50 100     1599 $val = $val->[0] if ref($val) eq 'ARRAY' && @$val == 1 && ref($template->{$key}) ne 'ARRAY';
      66        
1045             }
1046             }
1047 318         377 $i--;
1048 318 100       538 $val =~ s/%([A-F0-9])/chr(hex $1)/eig if ! ref $val;
  0         0  
1049              
1050 318 100       623 if (ref $template->{$key} eq 'ARRAY') {
1051 184 100       387 if (! defined $previously_set{$key}) {
1052 142         178 $previously_set{$key} = scalar @{ $template->{$key} };
  142         287  
1053             }
1054 184 100       349 next if $previously_set{$key};
1055 172 100       186 push @{ $template->{$key} }, ref($val) eq 'ARRAY' ? @$val : $val;
  172         948  
1056             } else {
1057 134 100       239 if (! defined $previously_set{$key}) {
1058 116 100       137 $previously_set{$key} = defined(${ $template->{$key} }) ? 1 : 0;
  116         324  
1059             }
1060 134 100       349 next if $previously_set{$key};
1061 107 50       189 die "Found multiple values on the configuration item \"$key\" which expects only one value" if ref($val) eq 'ARRAY';
1062 107         116 ${ $template->{$key} } = $val;
  107         361  
1063             }
1064             }
1065             }
1066             }
1067              
1068             sub _read_conf {
1069 9     9   16 my ($self, $file) = @_;
1070 9         11 my @args;
1071 9 50       31 $file = ($file =~ m|^([\w\.\-\/\\\:]+)$|) ? $1 : $self->fatal("Unsecure filename \"$file\"");
1072 9 50       289 open my $fh, '<', $file or do {
1073 0 0       0 $self->fatal("Couldn't open conf \"$file\" [$!]") if $ENV{'BOUND_SOCKETS'};
1074 0         0 warn "Couldn't open conf \"$file\" [$!]\n";
1075             };
1076 9         182 while (defined(my $line = <$fh>)) {
1077 189 100       404 $line = $1 if $line =~ /(.*?)(?
1078 189         207 $line =~ s/\\#/#/g;
1079 189 100       685 push @args, $1, $2 if $line =~ m/^\s*((?:--)?\w+)(?:\s*[=:]\s*|\s+)(.+)/;
1080             }
1081 9         97 close $fh;
1082 9         54 return \@args;
1083             }
1084              
1085             ###----------------------------------------------------------------###
1086              
1087       0 0   sub other_child_died_hook {}
1088              
1089       4 0   sub delete_child_hook {}
1090              
1091             sub delete_child {
1092 4     4 0 11 my ($self, $pid) = @_;
1093 4         8 my $prop = $self->{'server'};
1094              
1095 4 50       13 return $self->other_child_died_hook($pid) if ! exists $prop->{'children'}->{$pid};
1096              
1097             # prefork server check to clear child communication
1098 4 100       17 if ($prop->{'child_communication'}) {
1099 1 50       4 if ($prop->{'children'}->{$pid}->{'sock'}) {
1100 1         5 $prop->{'child_select'}->remove($prop->{'children'}->{$pid}->{'sock'});
1101 1         72 $prop->{'children'}->{$pid}->{'sock'}->close;
1102             }
1103             }
1104              
1105 4         92 $self->delete_child_hook($pid); # user customizable hook
1106              
1107 4         18 delete $prop->{'children'}->{$pid};
1108             }
1109              
1110             # send signal to all children - used by forking servers
1111             sub sig_pass {
1112 0     0 0 0 my ($self, $sig) = @_;
1113 0         0 foreach my $chld (keys %{ $self->{'server'}->{'children'} }) {
  0         0  
1114 0         0 $self->log(4, "signaling $chld with $sig" );
1115 0 0       0 kill($sig, $chld) || $self->log(1, "child $chld not signaled with $sig");
1116             }
1117             }
1118              
1119             # register sigs to allow passthrough to children
1120             sub register_sig_pass {
1121 3     3 0 9 my $self = shift;
1122 3   50     27 my $ref = $self->{'server'}->{'sig_passthrough'} || [];
1123 3 50       13 $ref = [$ref] if ! ref $ref;
1124 3 50       23 $self->fatal('invalid sig_passthrough') if ref $ref ne 'ARRAY';
1125 3 50       15 return if ! @$ref;
1126 0         0 $self->log(4, "sig_passthrough option found");
1127 0         0 require Net::Server::SIG;
1128 0         0 foreach my $sig (map {split /\s*,\s*/, $_} @$ref) {
  0         0  
1129 0         0 my $code = Net::Server::SIG::sig_is_registered($sig);
1130 0 0       0 if ($code) {
1131 0         0 $self->log(2, "Installing passthrough for $sig even though it is already registered.");
1132             } else {
1133 0 0       0 $code = ref($SIG{$sig}) eq 'CODE' ? $SIG{$sig} : undef;
1134             }
1135 0 0   0   0 Net::Server::SIG::register_sig($sig => sub { $self->sig_pass($sig); $code->($sig) if $code; });
  0         0  
  0         0  
1136 0         0 $self->log(2, "Installed passthrough for $sig");
1137             }
1138             }
1139              
1140             ###----------------------------------------------------------------###
1141              
1142             package Net::Server::TiedHandle;
1143 8     8   26 sub TIEHANDLE { my $pkg = shift; return bless [@_], $pkg }
  8         92  
1144 2 50   2   4 sub READLINE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'getline', @_) : $s->[0]->getline }
  2         38  
1145 0 0   0   0 sub SAY { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'say', @_) : $s->[0]->say(@_) }
  0         0  
1146 7 100   7   123 sub PRINT { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'print', @_) : $s->[0]->print(@_) }
  7         89  
1147 0 0   0     sub PRINTF { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'printf', @_) : $s->[0]->printf(@_) }
  0            
1148 0 0   0     sub READ { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'read', @_) : $s->[0]->read(@_) }
  0            
1149 0 0   0     sub WRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'write', @_) : $s->[0]->write(@_) }
  0            
1150 0 0   0     sub SYSREAD { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'sysread', @_) : $s->[0]->sysread(@_) }
  0            
1151 0 0   0     sub SYSWRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'syswrite', @_) : $s->[0]->syswrite(@_) }
  0            
1152 0 0   0     sub SEEK { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'seek', @_) : $s->[0]->seek(@_) }
  0            
1153       0     sub BINMODE {}
1154       0     sub FILENO {}
1155 0 0   0     sub CLOSE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'close', @_) : $s->[0]->close(@_) }
  0            
1156              
1157              
1158             1;
1159              
1160             ### The documentation is in Net/Server.pod