File Coverage

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