line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# -*- perl -*- |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Net::Server::Proto - Net::Server Protocol compatibility layer |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Copyright (C) 2001-2022 |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# Paul Seamons |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# This package may be distributed under the terms of either the |
10
|
|
|
|
|
|
|
# GNU General Public License |
11
|
|
|
|
|
|
|
# or the |
12
|
|
|
|
|
|
|
# Perl Artistic License |
13
|
|
|
|
|
|
|
# |
14
|
|
|
|
|
|
|
# All rights reserved. |
15
|
|
|
|
|
|
|
# |
16
|
|
|
|
|
|
|
################################################################ |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package Net::Server::Proto; |
19
|
|
|
|
|
|
|
|
20
|
43
|
|
|
43
|
|
21256
|
use strict; |
|
43
|
|
|
|
|
76
|
|
|
43
|
|
|
|
|
1013
|
|
21
|
43
|
|
|
43
|
|
174
|
use warnings; |
|
43
|
|
|
|
|
61
|
|
|
43
|
|
|
|
|
797
|
|
22
|
43
|
|
|
43
|
|
165
|
use Socket (); |
|
43
|
|
|
|
|
67
|
|
|
43
|
|
|
|
|
115136
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $requires_ipv6 = 0; |
25
|
|
|
|
|
|
|
my $ipv6_package; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub parse_info { |
28
|
100
|
|
|
100
|
0
|
312
|
my ($class, $port, $host, $proto, $ipv, $server) = @_; |
29
|
|
|
|
|
|
|
|
30
|
100
|
|
|
|
|
132
|
my $info; |
31
|
100
|
100
|
|
|
|
333
|
if (ref($port) eq 'HASH') { |
32
|
9
|
50
|
|
|
|
20
|
die "Missing port in hashref passed in port argument.\n" if ! $port->{'port'}; |
33
|
9
|
|
|
|
|
10
|
$info = $port; |
34
|
|
|
|
|
|
|
} else { |
35
|
91
|
|
|
|
|
135
|
$info = {}; |
36
|
91
|
100
|
|
|
|
278
|
$info->{'unix_type'} = $1 |
37
|
|
|
|
|
|
|
if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ (sock_stream|sock_dgram) \b }{}x; # legacy /some/path|sock_dgram |
38
|
91
|
100
|
|
|
|
367
|
$ipv = $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv* |
39
|
91
|
50
|
|
|
|
211
|
$ipv .= $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv4|IPv6 stacked |
40
|
91
|
100
|
66
|
|
|
581
|
$proto = $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ (tcp|udp|ssl|ssleay|unix|unixdgram|\w+(?: ::\w+)+) $ }{}xi # allow for 80/tcp or 200/udb or 90/Net::Server::Proto::TCP |
41
|
|
|
|
|
|
|
|| $port =~ s{ / (\w+) $ }{}x; # legacy 80/MyTcp support |
42
|
91
|
100
|
|
|
|
476
|
$host = $1 if $port =~ s{ ^ (.*?) [,|\s:]+ (?= \w+ $) }{}x; # allow localhost:80 |
43
|
91
|
|
|
|
|
220
|
$info->{'port'} = $port; |
44
|
|
|
|
|
|
|
} |
45
|
100
|
|
50
|
|
|
195
|
$info->{'port'} ||= 0; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
48
|
100
|
100
|
66
|
|
|
694
|
$info->{'host'} ||= (defined($host) && length($host)) ? $host : '*'; |
|
|
|
66
|
|
|
|
|
49
|
100
|
50
|
|
|
|
237
|
$ipv = $1 if $info->{'host'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv* |
50
|
100
|
50
|
|
|
|
229
|
$ipv .= $1 if $info->{'host'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv4|IPv6 stacked |
51
|
100
|
100
|
|
|
|
566
|
if ( $info->{'host'} =~ m{^ \[ ([\w/.\-:]+ | \*?) \] $ }x) { # allow for [::1] or [host.example.com] |
|
|
50
|
|
|
|
|
|
52
|
1
|
50
|
|
|
|
5
|
$info->{'host'} = length($1) ? $1 : '*'; |
53
|
|
|
|
|
|
|
} elsif ($info->{'host'} =~ m{^ ([\w/.\-:]+ | \*?) $ }x) { |
54
|
99
|
|
|
|
|
241
|
$info->{'host'} = $1; # untaint |
55
|
|
|
|
|
|
|
} else { |
56
|
0
|
|
|
|
|
0
|
$server->fatal("Could not determine host from \"$info->{'host'}\""); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
100
|
|
100
|
|
|
618
|
$info->{'proto'} ||= $proto || 'tcp'; |
|
|
|
66
|
|
|
|
|
61
|
100
|
50
|
|
|
|
180
|
$ipv = $1 if $info->{'proto'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv* |
62
|
100
|
50
|
|
|
|
173
|
$ipv .= $1 if $info->{'proto'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv4|IPv6 stacked |
63
|
100
|
50
|
|
|
|
429
|
if ($info->{'proto'} =~ /^(\w+ (?:::\w+)*)$/x) { |
64
|
100
|
|
|
|
|
218
|
$info->{'proto'} = $1; |
65
|
|
|
|
|
|
|
} else { |
66
|
0
|
|
|
|
|
0
|
$server->fatal("Could not determine proto from \"$proto\""); |
67
|
|
|
|
|
|
|
} |
68
|
100
|
|
|
|
|
208
|
$proto = lc $info->{'proto'}; |
69
|
|
|
|
|
|
|
|
70
|
100
|
100
|
|
|
|
205
|
if ($info->{'proto'} =~ /^UNIX/i) { |
71
|
8
|
|
|
|
|
55
|
return ({%$info, ipv => '*'}); |
72
|
|
|
|
|
|
|
} |
73
|
92
|
|
100
|
|
|
470
|
$ipv = $info->{'ipv'} || $ipv || $ENV{'IPV'} || ''; |
74
|
92
|
50
|
|
|
|
189
|
$ipv = join '', @$ipv if ref($ipv) eq 'ARRAY'; |
75
|
92
|
50
|
66
|
|
|
314
|
$server->fatal("Invalid ipv parameter - must contain 4, 6, or *") if $ipv && $ipv !~ /[46*]/; |
76
|
92
|
|
|
|
|
134
|
my @_info; |
77
|
92
|
100
|
100
|
|
|
572
|
if (!$ipv || $ipv =~ /[*]/) { |
|
|
50
|
33
|
|
|
|
|
78
|
41
|
|
|
|
|
49
|
my @rows = eval { $class->get_addr_info(@$info{qw(host port proto)}) }; |
|
41
|
|
|
|
|
106
|
|
79
|
41
|
50
|
0
|
|
|
82
|
$server->fatal($@ || "Could not find valid addresses for [$info->{'host'}]:$info->{'port'} with ipv set to '*'") if ! @rows; |
80
|
41
|
|
|
|
|
64
|
foreach my $row (@rows) { |
81
|
41
|
|
|
|
|
80
|
my ($host, $port, $ipv, $warn) = @$row; |
82
|
41
|
50
|
|
|
|
176
|
push @_info, {host => $host, port => $port, ipv => $ipv, proto => $info->{'proto'}, $warn ? (warn => $warn) : ()}; |
83
|
41
|
50
|
33
|
|
|
122
|
$requires_ipv6++ if $ipv ne '4' && $proto ne 'ssl'; # we need to know if Proto::TCP needs to reparent as a child of an IPv6 compatible socket library |
84
|
|
|
|
|
|
|
} |
85
|
41
|
50
|
33
|
|
|
86
|
if (@rows > 1 && $rows[0]->[1] == 0) { |
86
|
0
|
|
|
|
|
0
|
$server->log(2, "Determining auto-assigned port (0) for host $info->{'host'} (prebind)"); |
87
|
0
|
|
|
|
|
0
|
my $sock = $class->object($_info[-1], $server); |
88
|
0
|
|
|
|
|
0
|
$sock->connect($server); |
89
|
0
|
|
|
|
|
0
|
@$_{qw(port orig_port)} = ($sock->NS_port, 0) for @_info; |
90
|
|
|
|
|
|
|
} |
91
|
41
|
|
|
|
|
61
|
foreach my $_info (@_info) { |
92
|
|
|
|
|
|
|
$server->log(2, "Resolved [$info->{'host'}]:$info->{'port'} to [$_info->{'host'}]:$_info->{'port'}, IPv$_info->{'ipv'}") |
93
|
41
|
100
|
66
|
|
|
368
|
if $_info->{'host'} ne $info->{'host'} || $_info->{'port'} ne $info->{'port'}; |
94
|
41
|
50
|
|
|
|
205
|
$server->log(2, delete $_info->{'warn'}) if $_info->{'warn'}; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
} elsif ($ipv =~ /6/ || $info->{'host'} =~ /:/) { |
97
|
0
|
|
|
|
|
0
|
push @_info, {%$info, ipv => '6'}; |
98
|
0
|
0
|
|
|
|
0
|
$requires_ipv6++ if $proto ne 'ssl'; # IO::Socket::SSL does its own determination |
99
|
0
|
0
|
0
|
|
|
0
|
push @_info, {%$info, ipv => '4'} if $ipv =~ /4/ && $info->{'host'} !~ /:/; |
100
|
|
|
|
|
|
|
} else { |
101
|
51
|
|
|
|
|
273
|
push @_info, {%$info, ipv => '4'}; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
92
|
|
|
|
|
398
|
return @_info; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub get_addr_info { |
108
|
81
|
|
|
81
|
0
|
641
|
my ($class, $host, $port, $proto) = @_; |
109
|
81
|
50
|
|
|
|
273
|
$host = '*' if ! defined $host; |
110
|
81
|
100
|
|
|
|
208
|
$port = 0 if ! defined $port; |
111
|
81
|
100
|
|
|
|
202
|
$proto = 'tcp' if ! defined $proto; |
112
|
81
|
50
|
|
|
|
181
|
return ([$host, $port, '*']) if $proto =~ /UNIX/i; |
113
|
81
|
50
|
0
|
|
|
233
|
$port = (getservbyname($port, $proto))[2] or die "Could not determine port number from host [$host]:$_[2]\n" if $port =~ /\D/; |
114
|
|
|
|
|
|
|
|
115
|
81
|
|
|
|
|
111
|
my @info; |
116
|
81
|
100
|
33
|
|
|
338
|
if ($host =~ /^\d+(?:\.\d+){3}$/) { |
|
|
50
|
0
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
117
|
1
|
50
|
|
|
|
25
|
my $addr = Socket::inet_aton($host) or die "Unresolveable host [$host]:$port: invalid ip\n"; |
118
|
1
|
|
|
|
|
16
|
push @info, [Socket::inet_ntoa($addr), $port, 4] |
119
|
80
|
|
|
|
|
8624
|
} elsif (!$ENV{'NO_IPV6'} && eval { require Socket6 } && (eval { require IO::Socket::IP } || eval { require IO::Socket::INET6 })) { |
120
|
0
|
0
|
|
|
|
0
|
my $proto_id = getprotobyname(lc($proto) eq 'udp' ? 'udp' : 'tcp'); |
121
|
0
|
0
|
|
|
|
0
|
my $socktype = lc($proto) eq 'udp' ? Socket::SOCK_DGRAM() : Socket::SOCK_STREAM(); |
122
|
0
|
0
|
|
|
|
0
|
my @res = Socket6::getaddrinfo($host eq '*' ? '' : $host, $port, Socket::AF_UNSPEC(), $socktype, $proto_id, Socket6::AI_PASSIVE()); |
123
|
0
|
0
|
|
|
|
0
|
die "Unresolveable [$host]:$port: $res[0]\n" if @res < 5; |
124
|
0
|
|
|
|
|
0
|
while (@res >= 5) { |
125
|
0
|
|
|
|
|
0
|
my ($afam, $socktype, $proto, $saddr, $canonname) = splice @res, 0, 5; |
126
|
0
|
|
|
|
|
0
|
my @res2 = Socket6::getnameinfo($saddr, Socket6::NI_NUMERICHOST() | Socket6::NI_NUMERICSERV()); |
127
|
0
|
0
|
|
|
|
0
|
die "getnameinfo failed on [$host]:$port: $res2[0]\n" if @res2 < 2; |
128
|
0
|
|
|
|
|
0
|
my ($ip, $port) = @res2; |
129
|
0
|
0
|
|
|
|
0
|
my $ipv = ($afam == Socket6::AF_INET6()) ? 6 : ($afam == Socket::AF_INET()) ? 4 : '*'; |
|
|
0
|
|
|
|
|
|
130
|
0
|
|
|
|
|
0
|
push @info, [$ip, $port, $ipv]; |
131
|
|
|
|
|
|
|
} |
132
|
0
|
0
|
|
|
|
0
|
my %ipv6mapped = map {$_->[0] eq '::' ? ('0.0.0.0' => $_) : $_->[0] =~ /^::ffff:(\d+(?:\.\d+){3})$/ ? ($1 => $_) : ()} @info; |
|
0
|
0
|
|
|
|
0
|
|
133
|
0
|
0
|
0
|
|
|
0
|
if ((scalar(keys %ipv6mapped) |
|
|
|
0
|
|
|
|
|
134
|
0
|
|
|
|
|
0
|
&& grep {$ipv6mapped{$_->[0]}} @info) |
135
|
|
|
|
|
|
|
&& not my $only = $class->_bindv6only) { |
136
|
0
|
|
|
|
|
0
|
for my $i4 (@info) { |
137
|
0
|
|
0
|
|
|
0
|
my $i6 = $ipv6mapped{$i4->[0]} || next; |
138
|
0
|
0
|
0
|
|
|
0
|
if ($host eq '*' && $i6->[0] eq '::' && !length($only) |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
139
|
0
|
0
|
|
|
|
0
|
&& !eval{IO::Socket::INET6->new->configure({LocalAddr => '', LocalPort => 0, Listen => 1, ReuseAddr => 1, Domain => Socket6::AF_INET6()}) or die $!}) { |
140
|
0
|
|
|
|
|
0
|
$i4->[3] = "Host [*] resolved to IPv6 address [::] but IO::Socket::INET6->new fails: $@"; |
141
|
0
|
|
|
|
|
0
|
$i6->[0] = ''; |
142
|
|
|
|
|
|
|
} else { |
143
|
0
|
0
|
|
|
|
0
|
$i6->[3] = "Not including resolved host [$i4->[0]] IPv4 because it ".(length($only) ? 'will' : 'should')." be handled by [$i6->[0]] IPv6"; |
144
|
0
|
|
|
|
|
0
|
$i4->[0] = ''; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
0
|
|
|
|
|
0
|
@info = grep {length $_->[0]} @info; |
|
0
|
|
|
|
|
0
|
|
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
} elsif ($host =~ /:/) { |
150
|
0
|
|
|
|
|
0
|
die "Unresolveable host [$host]:$port - could not load IO::Socket::INET6: $@"; |
151
|
|
|
|
|
|
|
} else { |
152
|
80
|
|
|
|
|
190
|
my @addr; |
153
|
80
|
100
|
|
|
|
186
|
if ($host eq '*') { |
154
|
40
|
|
|
|
|
69
|
push @addr, Socket::INADDR_ANY(); |
155
|
|
|
|
|
|
|
} else { |
156
|
40
|
|
|
|
|
16382
|
(undef, undef, undef, undef, @addr) = gethostbyname($host); |
157
|
40
|
50
|
|
|
|
237
|
die "Unresolveable host [$host]:$port via IPv4 gethostbyname\n" if !@addr; |
158
|
|
|
|
|
|
|
} |
159
|
80
|
|
|
|
|
716
|
push @info, [Socket::inet_ntoa($_), $port, 4] for @addr |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
81
|
|
|
|
|
400
|
return @info; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub _bindv6only { |
166
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
167
|
0
|
|
|
|
|
0
|
my $val = $class->_sysctl('net.ipv6.bindv6only'); # linux |
168
|
0
|
0
|
|
|
|
0
|
$val = $class->_sysctl('net.inet6.ip6.v6only') if ! length($val); # bsd |
169
|
0
|
|
|
|
|
0
|
return $val; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub _sysctl { |
173
|
0
|
|
|
0
|
|
0
|
my ($class, $key) = @_; |
174
|
0
|
|
|
|
|
0
|
(my $file = "/proc/sys/$key") =~ y|.|/|; |
175
|
0
|
0
|
|
|
|
0
|
if (-e $file) { |
|
|
0
|
|
|
|
|
|
176
|
0
|
0
|
|
|
|
0
|
open my $fh, "<", $file or return ''; |
177
|
0
|
|
0
|
|
|
0
|
my $val = <$fh> || return ''; |
178
|
0
|
|
|
|
|
0
|
chomp $val; |
179
|
0
|
|
|
|
|
0
|
return $val; |
180
|
|
|
|
|
|
|
} elsif (-x "/sbin/sysctl") { |
181
|
0
|
|
|
|
|
0
|
my $val = (split /\s+/, `/sbin/sysctl -n $key 2>/dev/null`)[0]; |
182
|
0
|
0
|
|
|
|
0
|
return defined($val) ? $val : ''; |
183
|
|
|
|
|
|
|
} |
184
|
0
|
|
|
|
|
0
|
return ''; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub object { |
188
|
100
|
|
|
100
|
1
|
177
|
my ($class, $info, $server) = @_; |
189
|
100
|
|
|
|
|
164
|
my $proto_class = $info->{'proto'}; |
190
|
100
|
100
|
|
|
|
297
|
if ($proto_class !~ /::/) { |
191
|
99
|
50
|
|
|
|
415
|
$server->fatal("Invalid proto class \"$proto_class\"") if $proto_class !~ /^\w+$/; |
192
|
99
|
|
|
|
|
237
|
$proto_class = "Net::Server::Proto::" .uc($proto_class); |
193
|
|
|
|
|
|
|
} |
194
|
100
|
|
|
|
|
463
|
(my $file = "${proto_class}.pm") =~ s|::|/|g; |
195
|
100
|
50
|
|
|
|
169
|
$server->fatal("Unable to load module for proto \"$proto_class\": $@") if ! eval { require $file }; |
|
100
|
|
|
|
|
14746
|
|
196
|
100
|
|
|
|
|
576
|
return $proto_class->object($info, $server); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
107
|
50
|
|
107
|
0
|
343
|
sub requires_ipv6 { $requires_ipv6 ? 1 : undef } |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub ipv6_package { |
202
|
0
|
|
|
0
|
0
|
|
my ($class, $server) = @_; |
203
|
0
|
0
|
|
|
|
|
return $ipv6_package if $ipv6_package; |
204
|
|
|
|
|
|
|
|
205
|
0
|
0
|
|
|
|
|
eval { require Socket6 } |
|
0
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
or $server->fatal("Port configuration using IPv6 could not be started becauses of Socket6 library issues: $@"); |
207
|
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
|
my $pkg = $server->{'server'}->{'ipv6_package'}; |
209
|
0
|
0
|
|
|
|
|
if ($pkg) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
(my $file = "$pkg.pm") =~ s|::|/|g; |
211
|
0
|
0
|
|
|
|
|
eval { require $file } or $server->fatal("Could not load ipv6_package $pkg: $@"); |
|
0
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
} elsif ($INC{'IO/Socket/IP.pm'}) { # already loaded |
213
|
0
|
|
|
|
|
|
$pkg = 'IO::Socket::IP'; |
214
|
|
|
|
|
|
|
} elsif ($INC{'IO/Socket/INET6.pm'}) { |
215
|
0
|
|
|
|
|
|
$pkg = 'IO::Socket::INET6'; |
216
|
0
|
|
|
|
|
|
} elsif (eval { require IO::Socket::IP }) { |
217
|
0
|
|
|
|
|
|
$pkg = 'IO::Socket::IP'; |
218
|
|
|
|
|
|
|
} else { |
219
|
0
|
|
|
|
|
|
my $err = $@; |
220
|
0
|
0
|
|
|
|
|
if (eval { require IO::Socket::INET6 }) { |
|
0
|
|
|
|
|
|
|
221
|
0
|
|
|
|
|
|
$pkg = 'IO::Socket::INET6'; |
222
|
|
|
|
|
|
|
} else { |
223
|
0
|
|
|
|
|
|
$server->fatal("Port ocnfiguration using IPv6 could not be started. Could not find or load IO::Socket::IP or IO::Socket::INET6:\n $err $@") |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
0
|
|
|
|
|
|
return $ipv6_package = $pkg; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
1; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
__END__ |