File Coverage

blib/lib/Net/Server/Proto/SSL.pm
Criterion Covered Total %
statement 105 153 68.6
branch 38 84 45.2
condition 5 26 19.2
subroutine 19 22 86.3
pod 2 15 13.3
total 169 300 56.3


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