File Coverage

blib/lib/Net/Server/Proto/SSL.pm
Criterion Covered Total %
statement 108 159 67.9
branch 38 84 45.2
condition 5 32 15.6
subroutine 20 23 86.9
pod 2 15 13.3
total 173 313 55.2


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             # Net::Server::Proto::SSL - Net::Server Protocol module
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::SSL;
19              
20 3     3   47407 use strict;
  3         7  
  3         108  
21 3     3   14 use warnings;
  3         96  
  3         365  
22 3     3   16 use Net::Server::Proto qw(SOMAXCONN AF_INET AF_INET6 AF_UNSPEC);
  3         6  
  3         27  
23              
24             BEGIN {
25             # IO::Socket::SSL will automatically become IO::Socket::IP if it is available.
26             # This is different from Net::Server::Proto::SSLEAY that only does it if IPv6 is requested.
27 3 50   3   10 if (! eval { local $^W=0; require IO::Socket::SSL; 1 }) { # Quiet "redefined" warnings in case IO::Socket::INET6 <= 2.66
  3         11  
  3         3192  
  3         273194  
28 0         0 die "Module IO::Socket::SSL is required for SSL - you may alternately try SSLEAY. $@";
29             }
30             }
31              
32             our @ISA = qw(IO::Socket::SSL);
33             our $AUTOLOAD;
34              
35             my @ssl_args = qw(
36             SSL_use_cert
37             SSL_verify_mode
38             SSL_key_file
39             SSL_cert_file
40             SSL_ca_path
41             SSL_ca_file
42             SSL_cipher_list
43             SSL_passwd_cb
44             SSL_max_getline_length
45             SSL_error_callback
46             SSL_verify_callback
47             SSL_version
48             );
49              
50 8     8 0 51 sub NS_proto { 'SSL' }
51 8 100   8 0 16 sub NS_port { my $sock = shift; ${*$sock}{'NS_port'} = shift if @_; return ${*$sock}{'NS_port'} }
  8         19  
  3         10  
  8         13  
  8         105  
52 8 100   8 0 19 sub NS_host { my $sock = shift; ${*$sock}{'NS_host'} = shift if @_; return ${*$sock}{'NS_host'} }
  8         44  
  3         20  
  8         12  
  8         35  
53 8 100   8 0 210 sub NS_ipv { my $sock = shift; ${*$sock}{'NS_ipv'} = shift if @_; return ${*$sock}{'NS_ipv'} }
  8         27  
  3         28  
  8         16  
  8         41  
54 4 100   4 0 9 sub NS_listen { my $sock = shift; ${*$sock}{'NS_listen'} = shift if @_; return ${*$sock}{'NS_listen'} }
  4         10  
  2         15  
  4         6  
  4         11  
55              
56             sub object {
57 2     2 0 9 my ($class, $info, $server) = @_;
58              
59 2   33     32 my $ssl = $server->{'server'}->{'ssl_args'} ||= do {
60 2         19 my %temp = map {$_ => undef} @ssl_args;
  24         168  
61 2         7 $server->configure({map {$_ => \$temp{$_}} @ssl_args});
  24         75  
62 2         29 \%temp;
63             };
64              
65 2         95 my $sock = $class->new;
66 2         671 $sock->NS_host($info->{'host'});
67 2         30 $sock->NS_port($info->{'port'});
68 2         12 $sock->NS_ipv( $info->{'ipv'} );
69             $sock->NS_listen(defined($info->{'listen'}) ? $info->{'listen'}
70 2 50       15 : defined($server->{'server'}->{'listen'}) ? $server->{'server'}->{'listen'}
    50          
71             : SOMAXCONN);
72 2 50       7 ${*$sock}{'NS_orig_port'} = $info->{'orig_port'} if defined $info->{'orig_port'};
  0         0  
73              
74 2         4 my %seen;
75 2         10 for my $key (grep {!$seen{$_}++} (@ssl_args, sort grep {/^SSL_/} keys %$info)) { # allow for any SSL_ arg to get passed in via server configurations
  24         64  
  8         31  
76             my $val = defined($info->{$key}) ? $info->{$key}
77             : defined($ssl->{$key}) ? $ssl->{$key}
78 24 100       131 : $server->can($key) ? $server->$key($info->{'host'}, $info->{'port'}, 'SSL')
    100          
    50          
79             : undef;
80 24 100       57 next if ! defined $val;
81 3 50       57 $sock->$key($val) if defined $val;
82             }
83 2         18 return $sock;
84             }
85              
86             sub log_connect {
87 1     1 0 4 my ($sock, $server) = @_;
88 1         11 $server->log(2, "Binding to ".$sock->NS_proto." port ".$sock->NS_port." on host ".$sock->NS_host." with IPv".($sock->NS_ipv));
89             }
90              
91             sub connect {
92 1     1 1 3 my ($sock, $server) = @_;
93 1         2 my $host = $sock->NS_host;
94 1         4 my $port = $sock->NS_port;
95 1         4 my $ipv = $sock->NS_ipv;
96 1         3 my $lstn = $sock->NS_listen;
97              
98             $sock->SUPER::configure({
99             LocalPort => $port,
100             Proto => 'tcp',
101             Listen => $lstn,
102             ReuseAddr => 1,
103             Reuse => 1,
104             Family => ($ipv eq '6' ? AF_INET6 : $ipv eq '4' ? AF_INET : AF_UNSPEC),
105             (($host ne '*') ? (LocalAddr => $host) : ()), # * is all
106 1 50       8 (map {$_ => $sock->$_();} grep {/^SSL_/} keys %{*$sock}),
  2 50       5  
  7 50       31  
  1 50       5  
107             SSL_server => 1,
108             SSL_startHandshake => 0,
109             }) or $server->fatal("Cannot connect to SSL port $port on $host [$!]");
110              
111 1 50 33     129087 if ($port eq '0' and $port = $sock->sockport) {
    50 33        
112 0         0 $server->log(2, " Bound to auto-assigned port $port");
113 0         0 ${*$sock}{'NS_orig_port'} = $sock->NS_port;
  0         0  
114 0         0 $sock->NS_port($port);
115             } elsif ($port =~ /\D/ and $port = $sock->sockport) {
116 0         0 $server->log(2, " Bound to service port ".$sock->NS_port()."($port)");
117 0         0 ${*$sock}{'NS_orig_port'} = $sock->NS_port;
  0         0  
118 0         0 $sock->NS_port($port);
119             }
120             }
121              
122             sub reconnect { # after a sig HUP
123 0     0 0 0 my ($sock, $fd, $server, $port) = @_;
124 0         0 $server->log(3,"Reassociating file descriptor $fd with ".$sock->NS_proto." on [".$sock->NS_host."]:".$sock->NS_port.", using IPv".$sock->NS_ipv);
125              
126             $sock->configure_SSL({
127 0         0 (map {$_ => $sock->$_();} grep {/^SSL_/} keys %{*$sock}),
  0         0  
  0         0  
  0         0  
128             SSL_server => 1,
129             SSL_startHandshake => 0,
130             });
131 0 0       0 $sock->IO::Socket::INET::fdopen($fd, 'w') or $server->fatal("Error opening to file descriptor ($fd) [$!]");
132              
133 0         0 my $ipv = $sock->NS_ipv;
134 0 0       0 ${*$sock}{'io_socket_domain'} = $ipv eq '6' ? AF_INET6 : $ipv eq '4' ? AF_INET : AF_UNSPEC;
  0 0       0  
135              
136 0 0       0 if ($port ne $sock->NS_port) {
137 0         0 $server->log(2, " Re-bound to previously assigned port $port");
138 0         0 ${*$sock}{'NS_orig_port'} = $sock->NS_port;
  0         0  
139 0         0 $sock->NS_port($port);
140             }
141             }
142              
143             sub accept {
144 1     1 1 16 my ($sock, $class) = @_;
145 1         13 my ($client, $peername);
146             # SSL_startHandshake = 0 introduced in 1.994 makes accept not call accept_SSL
147 1 50       33 if ($IO::Socket::SSL::VERSION < 1.994) {
    50          
148 0 0       0 my $code = $sock->isa('IO::Socket::IP') ? 'IO::Socket::IP'->can('accept')
    0          
149             : $sock->isa('IO::Socket::INET6') ? 'IO::Socket::INET6'->can('accept')
150             : 'IO::Socket::INET'->can('accept'); # TODO - cache this lookup
151 0 0       0 if (wantarray) {
152 0   0     0 ($client, $peername) = $code->($sock, $class || ref($sock));
153             } else {
154 0   0     0 $client = $code->($sock, $class || ref($sock));
155             }
156             } elsif (wantarray) {
157 0   0     0 ($client, $peername) = $sock->SUPER::accept($class || ref($sock));
158             } else {
159 1   33     32 $client = $sock->SUPER::accept($class || ref($sock));
160             }
161 1         67473 ${*$client}{'_parent_sock'} = $sock;
  1         26  
162              
163 1 50       6 if (defined $client) {
164 1         8 $client->NS_proto($sock->NS_proto);
165 1         15 $client->NS_ipv( $sock->NS_ipv);
166 1         8 $client->NS_host( $sock->NS_host);
167 1         6 $client->NS_port( $sock->NS_port);
168             }
169              
170 1 50       33 return wantarray ? ($client, $peername) : $client;
171             }
172              
173             sub hup_string {
174 1     1 0 405 my $sock = shift;
175 1 50       4 return join "|", $sock->NS_host, $sock->NS_port, $sock->NS_proto, 'ipv'.$sock->NS_ipv, (defined(${*$sock}{'NS_orig_port'}) ? ${*$sock}{'NS_orig_port'} : ());
  1         11  
  0         0  
176             }
177              
178             sub show {
179 0     0 0 0 my $sock = shift;
180 0         0 my $t = "Ref = \"".ref($sock). "\" (".$sock->hup_string.")\n";
181 0         0 foreach my $prop (qw(SSLeay_context SSLeay_is_client)) {
182 0         0 $t .= " $prop = \"" .$sock->$prop()."\"\n";
183             }
184 0         0 return $t;
185             }
186              
187             sub AUTOLOAD {
188 3     3   8 my $sock = shift;
189 3 50       44 my $prop = $AUTOLOAD =~ /::([^:]+)$/ ? $1 : die "Missing property in AUTOLOAD.";
190 3 50       28 die "Unknown method or property [$prop]" if $prop !~ /^(SSL_\w+)$/;
191              
192 3     3   33 no strict 'refs';
  3         7  
  3         1226  
193 3         16 *{__PACKAGE__."::${prop}"} = sub {
194 6     6   14 my $sock = shift;
195 6 100       20 if (@_) {
196 3         5 ${*$sock}{$prop} = shift;
  3         23  
197 3 50       10 return delete ${*$sock}{$prop} if ! defined ${*$sock}{$prop};
  0         0  
  3         36  
198             } else {
199 3         5 return ${*$sock}{$prop};
  3         279  
200             }
201 3         21 };
202 3         10 return $sock->$prop(@_);
203             }
204              
205 1     1 0 7 sub tie_stdout { 1 }
206              
207             sub post_accept {
208 1     1 0 3 my $client = shift;
209 1 50       2 $client->_accept_ssl if !${*$client}{'_accept_ssl'};
  1         8  
210             }
211              
212             sub _accept_ssl {
213 1     1   3 my $client = shift;
214 1         6 ${*$client}{'_accept_ssl'} = 1;
  1         6  
215 1   50     3 my $sock = delete(${*$client}{'_parent_sock'}) || die "Could not get handshake from accept\n";
216 1 50       15 $sock->accept_SSL($client) || die "Could not finalize SSL connection with client handle ($@)\n";
217             }
218              
219             sub read_until { # allow for an interface that can be tied to STDOUT
220 0     0 0   my ($client, $bytes, $end_qr) = @_;
221 0 0 0       die "One of bytes or end_qr should be defined for TCP read_until\n" if !defined($bytes) && !defined($end_qr);
222              
223 0 0         $client->_accept_ssl if !${*$client}{'_accept_ssl'};
  0            
224              
225 0           my $content = '';
226 0           my $ok = 0;
227 0           while (1) {
228 0           $client->read($content, 1, length($content));
229 0 0 0       if (defined($bytes) && length($content) >= $bytes) {
    0 0        
230 0           $ok = 2;
231 0           last;
232             } elsif (defined($end_qr) && $content =~ $end_qr) {
233 0           $ok = 1;
234 0           last;
235             }
236             }
237 0 0         return wantarray ? ($ok, $content) : $content;
238             }
239              
240             1;
241              
242             =head1 NAME
243              
244             Net::Server::Proto::SSL - Net::Server SSL protocol.
245              
246             =head1 SYNOPSIS
247              
248             Until this release, it was preferable to use the Net::Server::Proto::SSLEAY
249             module. Recent versions include code that overcomes original limitations.
250              
251             See L.
252             See L.
253              
254             use base qw(Net::Server::HTTP);
255             main->run(
256             proto => 'ssl',
257             SSL_key_file => "/path/to/my/file.key",
258             SSL_cert_file => "/path/to/my/file.crt",
259             );
260              
261              
262             # OR
263              
264             sub SSL_key_file { "/path/to/my/file.key" }
265             sub SSL_cert_file { "/path/to/my/file.crt" }
266             main->run(proto = 'ssl');
267              
268              
269             # OR
270              
271             main->run(
272             port => [443, 8443, "80/tcp"], # bind to two ssl ports and one tcp
273             proto => "ssl", # use ssl as the default
274             ipv => "*", # bind both IPv4 and IPv6 interfaces
275             SSL_key_file => "/path/to/my/file.key",
276             SSL_cert_file => "/path/to/my/file.crt",
277             );
278              
279              
280             # OR
281              
282             main->run(port => [{
283             port => "443",
284             proto => "ssl",
285             # ipv => 4, # default - only do IPv4
286             SSL_key_file => "/path/to/my/file.key",
287             SSL_cert_file => "/path/to/my/file.crt",
288             }, {
289             port => "8443",
290             proto => "ssl",
291             ipv => "*", # IPv4 and IPv6
292             SSL_key_file => "/path/to/my/file2.key", # separate key
293             SSL_cert_file => "/path/to/my/file2.crt", # separate cert
294              
295             SSL_foo => 1, # Any key prefixed with SSL_ passed as a port hashref
296             # key/value will automatically be passed to IO::Socket::SSL
297             }]);
298              
299              
300             =head1 DESCRIPTION
301              
302             Protocol module for Net::Server based on IO::Socket::SSL. This module
303             implements a secure socket layer over tcp (also known as SSL) via the
304             IO::Socket::SSL module. If this module does not work in your
305             situation, please also consider using the SSLEAY protocol
306             (Net::Server::Proto::SSLEAY) which interfaces directly with
307             Net::SSLeay. See L.
308              
309             If you know that your server will only need IPv4 (which is the default
310             for Net::Server), you can load IO::Socket::SSL in inet4 mode which
311             will prevent it from using IO::Socket::IP or IO::Socket::INET6 since they
312             would represent additional and unused overhead.
313              
314             use IO::Socket::SSL qw(inet4);
315             use base qw(Net::Server::Fork);
316              
317             __PACKAGE__->run(proto => "ssl");
318              
319             =head1 PARAMETERS
320              
321             In addition to the normal Net::Server parameters, any of the SSL
322             parameters from IO::Socket::SSL may also be specified. See
323             L for information on setting this up. All arguments
324             prefixed with SSL_ will be passed to the IO::Socket::SSL->configure
325             method.
326              
327             =head1 BUGS
328              
329             Until version Net::Server version 2, Net::Server::Proto::SSL used the
330             default IO::Socket::SSL::accept method. This old approach introduces a
331             DDOS vulnerability into the server, where the socket is accepted, but
332             the parent server then has to block until the client negotiates the
333             SSL connection. This has now been overcome by overriding the accept
334             method and accepting the SSL negotiation after the parent socket has
335             had the chance to go back to listening.
336              
337             =head1 LICENCE
338              
339             Distributed under the same terms as Net::Server
340              
341             =head1 THANKS
342              
343             Thanks to Vadim for pointing out the IO::Socket::SSL accept
344             was returning objects blessed into the wrong class.
345              
346             =cut