File Coverage

blib/lib/IO/Async/SSL.pm
Criterion Covered Total %
statement 148 153 96.7
branch 55 70 78.5
condition 29 45 64.4
subroutine 25 26 96.1
pod 0 6 0.0
total 257 300 85.6


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2010-2017 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::SSL;
7              
8 11     11   1007801 use strict;
  11         124  
  11         295  
9 11     11   57 use warnings;
  11         17  
  11         472  
10              
11             our $VERSION = '0.21';
12             $VERSION = eval $VERSION;
13              
14 11     11   60 use Carp;
  11         18  
  11         684  
15              
16 11     11   5746 use IO::Socket::SSL 1.968 qw( $SSL_ERROR SSL_WANT_READ SSL_WANT_WRITE ); # default_ca
  11         454581  
  11         79  
17 11     11   1933 use POSIX qw( EAGAIN EWOULDBLOCK );
  11         21  
  11         85  
18              
19 11     11   4223 use Future 0.33; # ->catch_with_f
  11         61345  
  11         449  
20 11     11   3514 use IO::Async::Handle 0.29;
  11         73105  
  11         416  
21 11     11   603 use IO::Async::Loop '0.61'; # new Listen API
  11         7117  
  11         15494  
22              
23             =head1 NAME
24              
25             C - use SSL/TLS with L
26              
27             =head1 SYNOPSIS
28              
29             use IO::Async::Loop;
30             use IO::Async::SSL;
31              
32             my $loop = IO::Async::Loop->new();
33              
34             $loop->SSL_connect(
35             host => "www.example.com",
36             service => "https",
37              
38             on_stream => sub {
39             my ( $stream ) = @_;
40              
41             $stream->configure(
42             on_read => sub {
43             ...
44             },
45             );
46              
47             $loop->add( $stream );
48              
49             ...
50             },
51              
52             on_resolve_error => sub { print STDERR "Cannot resolve - $_[0]\n"; },
53             on_connect_error => sub { print STDERR "Cannot connect\n"; },
54             on_ssl_error => sub { print STDERR "Cannot negotiate SSL - $_[-1]\n"; },
55             );
56              
57             =head1 DESCRIPTION
58              
59             This module extends existing L classes with extra methods to allow
60             the use of SSL or TLS-based connections using L. It does not
61             directly provide any methods or functions of its own.
62              
63             Primarily, it provides C and C, which yield
64             C-upgraded socket handles or L
65             instances, and two forms of C to upgrade an existing TCP
66             connection to use SSL.
67              
68             As an additional convenience, if the C and C
69             options are omitted, the module will attempt to provide them by quering the
70             result of L's C function. Otherwise, the module
71             will print a warning and set C instead.
72              
73             =cut
74              
75             my %SSL_ca_args = IO::Socket::SSL::default_ca();
76              
77             sub _SSL_args
78             {
79 33     33   160 my %args = @_;
80              
81             # SSL clients (i.e. non-server) require a verify mode
82 33 100 100     164 if( !$args{SSL_server} and !defined $args{SSL_verify_mode} and
      66        
      66        
83             !defined $args{SSL_ca_file} and !defined $args{SSL_ca_path} ) {
84 1 50       3 unless( %SSL_ca_args ) {
85 0         0 carp "Unable to set SSL_VERIFY_PEER because IO::Socket::SSL::default_ca() gives nothing";
86 0         0 $SSL_ca_args{SSL_verify_mode} = IO::Socket::SSL::SSL_VERIFY_NONE();
87             }
88              
89 1         5 %args = ( %SSL_ca_args, %args );
90             }
91              
92 33         352 return %args;
93             }
94              
95             sub sslread
96             {
97 280     280 0 211846 my $stream = shift;
98 280         529 my ( $fh, undef, $len ) = @_;
99              
100 280         650 my $ret = $stream->_sysread( $fh, $_[1], $len );
101              
102 280   66     47203 my $read_wants_write = !defined $ret &&
103             ( $! == EAGAIN or $! == EWOULDBLOCK ) &&
104             $SSL_ERROR == SSL_WANT_WRITE;
105 280         904 $stream->want_writeready_for_read( $read_wants_write );
106              
107             # It's possible SSL_read took all the data out of the filehandle, thus
108             # making it not appear read-ready any more.
109 280 100       5491 if( $fh->pending ) {
110 16     16   230 $stream->loop->later( sub { $stream->on_read_ready } );
  16         3224  
111             }
112              
113 280         3472 return $ret;
114             }
115              
116             sub sslwrite
117             {
118 263     263 0 89276 my $stream = shift;
119 263         535 my ( $fh, undef, $len ) = @_;
120              
121             # Placate RT98372
122 263 50       793 utf8::downgrade( $_[1] ) or
123             carp "Wide character in sslwrite";
124              
125 263         625 my $ret = $stream->_syswrite( $fh, $_[1], $len );
126              
127 263   33     49835 my $write_wants_read = !defined $ret &&
128             ( $! == EAGAIN or $! == EWOULDBLOCK ) &&
129             $SSL_ERROR == SSL_WANT_READ;
130 263         809 $stream->want_readready_for_write( $write_wants_read );
131             # If write wants read, there's no point waiting on writereadiness either
132 263         4972 $stream->want_writeready_for_write( !$write_wants_read );
133              
134 263         4860 return $ret;
135             }
136              
137             =head1 LOOP METHODS
138              
139             The following extra methods are added to L.
140              
141             =cut
142              
143             =head2 SSL_upgrade
144              
145             ( $stream or $socket ) = $loop->SSL_upgrade( %params )->get;
146              
147             This method upgrades a given stream filehandle into an SSL-wrapped stream,
148             returning a future which will yield the given stream object or socket.
149              
150             Takes the following parameters:
151              
152             =over 8
153              
154             =item handle => IO::Async::Stream | IO
155              
156             The C object containing the IO handle of an
157             already-established connection to act as the transport for SSL; or the plain
158             IO socket handle itself.
159              
160             If an C is passed it will have the C and C
161             functions set on it suitable for SSL use, and will be returned as the result
162             from the future.
163              
164             If a plain socket handle is passed, that will be returned from the future
165             instead.
166              
167             =item SSL_server => BOOL
168              
169             If true, indicates this is the server side of the connection.
170              
171             =back
172              
173             In addition, any parameter whose name starts C will be passed to the
174             C constructor.
175              
176             The following legacy callback arguments are also supported, in case the
177             returned future is not used:
178              
179             =over 8
180              
181             =item on_upgraded => CODE
182              
183             A continuation that is invoked when the socket has been successfully upgraded
184             to SSL. It will be passed an instance of an C, which will
185             have appropriate SSL-compatible reader/writer functions attached.
186              
187             $on_upgraded->( $sslsocket )
188              
189             =item on_error => CODE
190              
191             A continuation that is invoked if C detects an error while
192             negotiating the upgrade.
193              
194             $on_error->( $! )
195              
196             =back
197              
198             =cut
199              
200             sub IO::Async::Loop::SSL_upgrade
201             {
202 22     22 0 12844 my $loop = shift;
203 22         95 my %params = @_;
204              
205 22         79 my $f = $loop->new_future;
206              
207 22 50       2773 $params{handle} or croak "Expected 'handle'";
208              
209 22         46 my $stream;
210             my $socket;
211 22 100       142 if( $params{handle}->isa( "IO::Async::Stream" ) ) {
212 16         35 $stream = delete $params{handle};
213 16         46 $socket = $stream->read_handle;
214             }
215             else {
216 6         13 $socket = delete $params{handle};
217             }
218              
219             {
220 22         95 my $on_upgraded = delete $params{on_upgraded} or defined wantarray
221 22 50 66     118 or croak "Expected 'on_upgraded' or to return a Future";
222             my $on_error = delete $params{on_error} or defined wantarray
223 22 50 66     104 or croak "Expected 'on_error' or to return a Future";
224              
225 22 100       91 $f->on_done( $on_upgraded ) if $on_upgraded;
226 22 100       211 $f->on_fail( $on_error ) if $on_error;
227             }
228              
229 22         264 my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;
  44         134  
230              
231             eval {
232             $socket = IO::Socket::SSL->start_SSL( $socket, _SSL_args
233             SSL_startHandshake => 0,
234              
235             # Required to make IO::Socket::SSL not ->close before we have a chance to remove it from the loop
236       4     SSL_error_trap => sub { },
237              
238 22 100       177 %ssl_params,
239             ) or die IO::Socket::SSL->errstr;
240 22 100       49 } or do {
241 1         443 chomp( my $e = $@ );
242 1         7 return $f->fail( $e, "ssl" );
243             };
244              
245 21 100       16593 my $ready_method = $ssl_params{SSL_server} ? "accept_SSL" : "connect_SSL";
246              
247             my $ready = sub {
248 54     54   19155 my ( $self ) = @_;
249 54 100       241 if( $socket->$ready_method ) {
250 17         4078 $loop->remove( $self );
251              
252 17 100       3428 if( $stream ) {
253 15         75 $stream->configure(
254             handle => $socket,
255             reader => \&sslread,
256             writer => \&sslwrite,
257             );
258             }
259              
260 17   66     2783 $f->done( $stream || $socket );
261 17         2402 return;
262             }
263              
264 37 100 66     30018 if( $! != EAGAIN and $! != EWOULDBLOCK ) {
265 4         13 my $errstr = IO::Socket::SSL::errstr();
266 4         39 $loop->remove( $self );
267 4         820 $f->fail( $errstr, "ssl" );
268 4         556 return;
269             }
270              
271 33         127 $self->want_readready ( $SSL_ERROR == SSL_WANT_READ );
272 33         303 $self->want_writeready( $SSL_ERROR == SSL_WANT_WRITE );
273 21         110 };
274              
275             # We're going to steal the IO handle from $stream, so we'll have to
276             # temporarily deconfigure it
277 21 100       100 $stream->configure( handle => undef ) if $stream;
278              
279 21         2206 $loop->add( my $handle = IO::Async::Handle->new(
280             handle => $socket,
281             on_read_ready => $ready,
282             on_write_ready => $ready,
283             ) );
284              
285 21         6264 $ready->( $handle );
286              
287 21 100       292 return $f if defined wantarray;
288              
289             # Caller is not going to keep hold of the Future, so we have to ensure it
290             # stays alive somehow
291 2     2   15 $f->on_ready( sub { undef $f } ); # intentional cycle
  2         36  
292             }
293              
294             =head2 SSL_connect
295              
296             $stream = $loop->SSL_connect( %params )->get;
297              
298             This method performs a non-blocking connection to a given address or set of
299             addresses, upgrades the socket to SSL, then yields a C
300             object when the SSL handshake is complete.
301              
302             It takes all the same arguments as C. Any argument
303             whose name starts C will be passed on to the L
304             constructor rather than the Loop's C method. It is not required to
305             pass the C option, as SSL implies this will be C.
306              
307             This method can also upgrade an existing C or subclass
308             instance given as the C argument, by setting the C and
309             C functions.
310              
311             =head2 SSL_connect (void)
312              
313             $loop->SSL_connect( %params,
314             on_connected => sub { ... },
315             on_stream => sub { ... },
316             );
317              
318             When not returning a future, this method also supports the C and
319             C continuations.
320              
321             In addition, the following arguments are then required:
322              
323             =over 8
324              
325             =item on_ssl_error => CODE
326              
327             A continuation that is invoked if C detects an SSL-based
328             error once the actual stream socket is connected.
329              
330             =back
331              
332             If the C continuation is used, the socket handle it yields will
333             be a C, which must be wrapped in C to
334             be used by C. The C continuation will already yield such
335             an instance.
336              
337             =cut
338              
339             sub IO::Async::Loop::SSL_connect
340             {
341 6     6 0 1041414 my $loop = shift;
342 6         44 my %params = @_;
343              
344 6         58 my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;
  5         21  
345              
346 6         24 my $on_done;
347 6 100       37 if( exists $params{on_connected} ) {
    100          
348 1         3 my $on_connected = delete $params{on_connected};
349             $on_done = sub {
350 0     0   0 my ( $stream ) = @_;
351 0         0 $on_connected->( $stream->read_handle );
352 1         12 };
353             }
354             elsif( exists $params{on_stream} ) {
355 1         3 my $on_stream = delete $params{on_stream};
356 1         2 $on_done = $on_stream;
357             }
358             else {
359 4 50       19 croak "Expected 'on_connected' or 'on_stream' or to return a Future" unless defined wantarray;
360             }
361              
362             my $on_ssl_error = delete $params{on_ssl_error} or defined wantarray or
363 6 50 66     49 croak "Expected 'on_ssl_error' or to return a Future";
364              
365 6   66     57 my $stream = delete $params{handle} || do {
366             require IO::Async::Stream;
367             IO::Async::Stream->new;
368             };
369              
370 6 50       328 $stream->isa( "IO::Async::Stream" ) or
371             croak "Can only SSL_connect a handle instance of IO::Async::Stream";
372              
373             # Don't ->connect with the handle yet, because we'll first have to use the
374             # socket to perform SSL_upgrade on. We don't want to confuse the loop by
375             # giving it the same fd twice.
376              
377             my $f = $loop->connect(
378             socktype => 'stream', # SSL over DGRAM or RAW makes no sense
379             %params,
380             )->then( sub {
381 6     6   16759 my ( $socket ) = @_;
382              
383 6         32 $stream->configure( handle => $socket );
384              
385 6         1869 $loop->SSL_upgrade(
386             _SSL_args( %ssl_params ),
387             handle => $stream,
388             )
389 6         58 });
390              
391 6 100       36438 $f->on_done( $on_done ) if $on_done;
392             $f->on_fail( sub {
393 1 50 33 1   140 $on_ssl_error->( $_[0] ) if defined $_[1] and $_[1] eq "ssl";
394 6 100       95 }) if $on_ssl_error;
395              
396 6 100       123 return $f if defined wantarray;
397              
398             # Caller is not going to keep hold of the Future, so we have to ensure it
399             # stays alive somehow
400 2     2   13 $f->on_ready( sub { undef $f } ); # intentional cycle
  2         102  
401             }
402              
403             =head2 SSL_listen
404              
405             $loop->SSL_listen( %params )->get;
406              
407             This method sets up a listening socket using the addresses given, and will
408             invoke the callback each time a new connection is accepted on the socket and
409             the SSL handshake has been completed. This can be either the C or
410             C continuation; C is not supported.
411              
412             It takes all the same arguments as C. Any argument
413             whose name starts C will be passed on to the L
414             constructor rather than the Loop's C method. It is not required to
415             pass the C option, as SSL implies this will be C.
416              
417             In addition, the following arguments are rquired:
418              
419             =over 8
420              
421             =item on_ssl_error => CODE
422              
423             A continuation that is invoked if C detects an SSL-based
424             error once the actual stream socket is connected.
425              
426             =back
427              
428             The underlying L socket will also require the server key and
429             certificate for a server-mode socket. See its documentation for more details.
430              
431             If the C continuation is used, the socket handle it yields will be
432             a C, which must be wrapped in C to be
433             used by C. The C continuation will already yield such an
434             instance.
435              
436             =cut
437              
438             sub IO::Async::Loop::SSL_listen
439             {
440 5     5 0 21351 my $loop = shift;
441 5         34 my %params = @_;
442              
443 5         52 my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;
  10         34  
444             my $on_ssl_error = delete $params{on_ssl_error} or defined wantarray
445 5 50 66     37 or croak "Expected 'on_ssl_error'";
446              
447             my $f = $loop->listen(
448             socktype => 'stream',
449             %params,
450             )->on_done( sub {
451 5     5   13550 my $listener = shift;
452              
453 5         78 my $cleartext_acceptor = $listener->acceptor;
454             my $ssl_acceptor = sub {
455 5         31090 my $listener = shift;
456 5         17 my ( $listen_sock, %params ) = @_;
457 5         12 my $stream = $params{handle};
458 5 50 66     46 !defined $stream or $stream->isa( "IO::Async::Stream" ) or
459             croak "Can only accept SSL on IO::Async::Stream handles";
460              
461             $listener->$cleartext_acceptor( $listen_sock )->then( sub {
462 5         1357 my ( $socket ) = @_;
463              
464 5 50       18 return Future->done() unless $socket; # EAGAIN
465              
466 5 100       31 $stream->configure( handle => $socket ) if $stream;
467              
468             $loop->SSL_upgrade(
469             _SSL_args( SSL_server => 1, %ssl_params ),
470             handle => ( $stream || $socket ),
471             )->catch_with_f( ssl => sub {
472 1         121 my ( $f, $failure ) = @_;
473 1 50       6 if( $on_ssl_error ) {
474 1         5 $on_ssl_error->( $failure );
475 1         10 return Future->done; # eat it
476             }
477 0         0 return $f;
478 5   66     571 });
479 5         23 });
480 5         76 };
481              
482 5         21 $listener->configure( acceptor => $ssl_acceptor );
483 5         41 });
484              
485 5 100       65032 return $f if defined wantarray;
486              
487             # Caller is not going to keep hold of the Future, so we have to ensure it
488             # stays alive somehow
489 2     2   27 $f->on_ready( sub { undef $f } ); # intentional cycle
  2         93  
490             }
491              
492             =head1 STREAM PROTOCOL METHODS
493              
494             The following extra methods are added to L.
495              
496             =cut
497              
498             =head2 SSL_upgrade
499              
500             $protocol->SSL_upgrade( %params )->get;
501              
502             A shortcut to calling C<< $loop->SSL_upgrade >>. This method will unconfigure
503             the C of the Protocol, upgrade its underlying filehandle to SSL,
504             then reconfigure it again with SSL reader and writer functions on it. It takes
505             the same arguments as C<< $loop->SSL_upgrade >>, except that the C
506             argument is not required as it's taken from the Protocol's C.
507              
508             =cut
509              
510             sub IO::Async::Protocol::Stream::SSL_upgrade
511             {
512 2     2 0 4595 my $protocol = shift;
513 2         9 my %params = @_;
514              
515 2 50       7 my $on_upgraded = delete $params{on_upgraded} or croak "Expected 'on_upgraded'";
516              
517 2 50       11 my $loop = $protocol->get_loop or croak "Expected to be a member of a Loop";
518              
519 2         13 my $transport = $protocol->transport;
520              
521 2         11 $protocol->configure( transport => undef );
522              
523             $loop->SSL_upgrade(
524             handle => $transport,
525             on_upgraded => sub {
526 2     2   100 my ( $transport ) = @_;
527              
528 2         7 $protocol->configure( transport => $transport );
529              
530 2         738 $on_upgraded->();
531             },
532              
533 2         728 %params,
534             );
535             }
536              
537             =head1 AUTHOR
538              
539             Paul Evans
540              
541             =cut
542              
543             0x55AA;