| 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 |