File Coverage

blib/lib/IO/Async/SSL.pm
Criterion Covered Total %
statement 146 153 95.4
branch 52 68 76.4
condition 29 45 64.4
subroutine 25 26 96.1
pod 0 6 0.0
total 252 298 84.5


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-2015 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::SSL;
7              
8 11     11   656019 use strict;
  11         26  
  11         325  
9 11     11   55 use warnings;
  11         25  
  11         501  
10              
11             our $VERSION = '0.20';
12             $VERSION = eval $VERSION;
13              
14 11     11   67 use Carp;
  11         23  
  11         727  
15              
16 11     11   8030 use IO::Socket::SSL 1.968 qw( $SSL_ERROR SSL_WANT_READ SSL_WANT_WRITE ); # default_ca
  11         460934  
  11         81  
17 11     11   1855 use POSIX qw( EAGAIN EWOULDBLOCK );
  11         24  
  11         92  
18              
19 11     11   6693 use Future 0.33; # ->catch_with_f
  11         83626  
  11         440  
20 11     11   4958 use IO::Async::Handle 0.29;
  11         81212  
  11         358  
21 11     11   682 use IO::Async::Loop '0.61'; # new Listen API
  11         8859  
  11         15906  
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 32     32   213 my %args = @_;
80              
81             # SSL clients (i.e. non-server) require a verify mode
82 32 100 100     237 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       4 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         4 %args = ( %SSL_ca_args, %args );
90             }
91              
92 32         447 return %args;
93             }
94              
95             sub sslread
96             {
97 280     280 0 229361 my $stream = shift;
98 280         810 my ( $fh, undef, $len ) = @_;
99              
100 280         1051 my $ret = $stream->_sysread( $fh, $_[1], $len );
101              
102 280   66     57914 my $read_wants_write = !defined $ret &&
103             ( $! == EAGAIN or $! == EWOULDBLOCK ) &&
104             $SSL_ERROR == SSL_WANT_WRITE;
105 280         1265 $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       7534 if( $fh->pending ) {
110 16     16   314 $stream->loop->later( sub { $stream->on_read_ready } );
  16         4565  
111             }
112              
113 280         5078 return $ret;
114             }
115              
116             sub sslwrite
117             {
118 263     263 0 107991 my $stream = shift;
119 263         771 my ( $fh, undef, $len ) = @_;
120              
121             # Placate RT98372
122 263 50       1181 utf8::downgrade( $_[1] ) or
123             carp "Wide character in sslwrite";
124              
125 263         989 my $ret = $stream->_syswrite( $fh, $_[1], $len );
126              
127 263   33     64774 my $write_wants_read = !defined $ret &&
128             ( $! == EAGAIN or $! == EWOULDBLOCK ) &&
129             $SSL_ERROR == SSL_WANT_READ;
130 263         1200 $stream->want_readready_for_write( $write_wants_read );
131             # If write wants read, there's no point waiting on writereadiness either
132 263         7225 $stream->want_writeready_for_write( !$write_wants_read );
133              
134 263         6517 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 21     21 0 14457 my $loop = shift;
203 21         130 my %params = @_;
204              
205 21         200 my $f = $loop->new_future;
206              
207 21 50       3827 $params{handle} or croak "Expected 'handle'";
208              
209 21         56 my $stream;
210             my $socket;
211 21 100       202 if( $params{handle}->isa( "IO::Async::Stream" ) ) {
212 16         48 $stream = delete $params{handle};
213 16         70 $socket = $stream->read_handle;
214             }
215             else {
216 5         19 $socket = delete $params{handle};
217             }
218              
219             {
220 21         109 my $on_upgraded = delete $params{on_upgraded} or defined wantarray
221 21 50 66     164 or croak "Expected 'on_upgraded' or to return a Future";
222             my $on_error = delete $params{on_error} or defined wantarray
223 21 50 66     162 or croak "Expected 'on_error' or to return a Future";
224              
225 21 100       111 $f->on_done( $on_upgraded ) if $on_upgraded;
226 21 100       295 $f->on_fail( $on_error ) if $on_error;
227             }
228              
229 21         386 my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;
  41         179  
230              
231             eval { $socket = IO::Socket::SSL->start_SSL( $socket, _SSL_args
232             SSL_startHandshake => 0,
233              
234             # Required to make IO::Socket::SSL not ->close before we have a chance to remove it from the loop
235       4     SSL_error_trap => sub { },
236              
237 21         325 %ssl_params,
238 21 50       65 ) } or do {
239 0         0 chomp( my $e = $@ );
240 0         0 return $f->fail( $e, "ssl" );
241             };
242              
243 21 100       22048 my $ready_method = $ssl_params{SSL_server} ? "accept_SSL" : "connect_SSL";
244              
245             my $ready = sub {
246 54     54   30918 my ( $self ) = @_;
247 54 100       446 if( $socket->$ready_method ) {
248 17         5188 $loop->remove( $self );
249              
250 17 100       4339 if( $stream ) {
251 15         143 $stream->configure(
252             handle => $socket,
253             reader => \&sslread,
254             writer => \&sslwrite,
255             );
256             }
257              
258 17   66     3958 $f->done( $stream || $socket );
259 17         3306 return;
260             }
261              
262 37 100 66     38399 if( $! != EAGAIN and $! != EWOULDBLOCK ) {
263 4         14 my $errstr = IO::Socket::SSL::errstr();
264 4         49 $loop->remove( $self );
265 4         900 $f->fail( $errstr, "ssl" );
266 4         503 return;
267             }
268              
269 33         198 $self->want_readready ( $SSL_ERROR == SSL_WANT_READ );
270 33         460 $self->want_writeready( $SSL_ERROR == SSL_WANT_WRITE );
271 21         163 };
272              
273             # We're going to steal the IO handle from $stream, so we'll have to
274             # temporarily deconfigure it
275 21 100       165 $stream->configure( handle => undef ) if $stream;
276              
277 21         3146 $loop->add( my $handle = IO::Async::Handle->new(
278             handle => $socket,
279             on_read_ready => $ready,
280             on_write_ready => $ready,
281             ) );
282              
283 21         9493 $ready->( $handle );
284              
285 21 100       525 return $f if defined wantarray;
286              
287             # Caller is not going to keep hold of the Future, so we have to ensure it
288             # stays alive somehow
289 2     2   38 $f->on_ready( sub { undef $f } ); # intentional cycle
  2         46  
290             }
291              
292             =head2 SSL_connect
293              
294             $stream = $loop->SSL_connect( %params )->get;
295              
296             This method performs a non-blocking connection to a given address or set of
297             addresses, upgrades the socket to SSL, then yields a C
298             object when the SSL handshake is complete.
299              
300             It takes all the same arguments as C. Any argument
301             whose name starts C will be passed on to the L
302             constructor rather than the Loop's C method. It is not required to
303             pass the C option, as SSL implies this will be C.
304              
305             This method can also upgrade an existing C or subclass
306             instance given as the C argument, by setting the C and
307             C functions.
308              
309             =head2 SSL_connect (void)
310              
311             $loop->SSL_connect( %params,
312             on_connected => sub { ... },
313             on_stream => sub { ... },
314             );
315              
316             When not returning a future, this method also supports the C and
317             C continuations.
318              
319             In addition, the following arguments are then required:
320              
321             =over 8
322              
323             =item on_ssl_error => CODE
324              
325             A continuation that is invoked if C detects an SSL-based
326             error once the actual stream socket is connected.
327              
328             =back
329              
330             If the C continuation is used, the socket handle it yields will
331             be a C, which must be wrapped in C to
332             be used by C. The C continuation will already yield such
333             an instance.
334              
335             =cut
336              
337             sub IO::Async::Loop::SSL_connect
338             {
339 6     6 0 1072557 my $loop = shift;
340 6         61 my %params = @_;
341              
342 6         86 my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;
  5         38  
343              
344 6         39 my $on_done;
345 6 100       43 if( exists $params{on_connected} ) {
    100          
346 1         4 my $on_connected = delete $params{on_connected};
347             $on_done = sub {
348 0     0   0 my ( $stream ) = @_;
349 0         0 $on_connected->( $stream->read_handle );
350 1         10 };
351             }
352             elsif( exists $params{on_stream} ) {
353 1         2 my $on_stream = delete $params{on_stream};
354 1         3 $on_done = $on_stream;
355             }
356             else {
357 4 50       28 croak "Expected 'on_connected' or 'on_stream' or to return a Future" unless defined wantarray;
358             }
359              
360             my $on_ssl_error = delete $params{on_ssl_error} or defined wantarray or
361 6 50 66     96 croak "Expected 'on_ssl_error' or to return a Future";
362              
363 6   66     81 my $stream = delete $params{handle} || do {
364             require IO::Async::Stream;
365             IO::Async::Stream->new;
366             };
367              
368 6 50       409 $stream->isa( "IO::Async::Stream" ) or
369             croak "Can only SSL_connect a handle instance of IO::Async::Stream";
370              
371             # Don't ->connect with the handle yet, because we'll first have to use the
372             # socket to perform SSL_upgrade on. We don't want to confuse the loop by
373             # giving it the same fd twice.
374              
375             my $f = $loop->connect(
376             socktype => 'stream', # SSL over DGRAM or RAW makes no sense
377             %params,
378             )->then( sub {
379 6     6   21753 my ( $socket ) = @_;
380              
381 6         45 $stream->configure( handle => $socket );
382              
383 6         1602 $loop->SSL_upgrade(
384             _SSL_args( %ssl_params ),
385             handle => $stream,
386             )
387 6         91 });
388              
389 6 100       55014 $f->on_done( $on_done ) if $on_done;
390             $f->on_fail( sub {
391 1 50 33 1   143 $on_ssl_error->( $_[0] ) if defined $_[1] and $_[1] eq "ssl";
392 6 100       97 }) if $on_ssl_error;
393              
394 6 100       174 return $f if defined wantarray;
395              
396             # Caller is not going to keep hold of the Future, so we have to ensure it
397             # stays alive somehow
398 2     2   15 $f->on_ready( sub { undef $f } ); # intentional cycle
  2         148  
399             }
400              
401             =head2 SSL_listen
402              
403             $loop->SSL_listen( %params )->get;
404              
405             This method sets up a listening socket using the addresses given, and will
406             invoke the callback each time a new connection is accepted on the socket and
407             the SSL handshake has been completed. This can be either the C or
408             C continuation; C is not supported.
409              
410             It takes all the same arguments as C. Any argument
411             whose name starts C will be passed on to the L
412             constructor rather than the Loop's C method. It is not required to
413             pass the C option, as SSL implies this will be C.
414              
415             In addition, the following arguments are rquired:
416              
417             =over 8
418              
419             =item on_ssl_error => CODE
420              
421             A continuation that is invoked if C detects an SSL-based
422             error once the actual stream socket is connected.
423              
424             =back
425              
426             The underlying L socket will also require the server key and
427             certificate for a server-mode socket. See its documentation for more details.
428              
429             If the C continuation is used, the socket handle it yields will be
430             a C, which must be wrapped in C to be
431             used by C. The C continuation will already yield such an
432             instance.
433              
434             =cut
435              
436             sub IO::Async::Loop::SSL_listen
437             {
438 5     5 0 30773 my $loop = shift;
439 5         45 my %params = @_;
440              
441 5         64 my %ssl_params = map { $_ => delete $params{$_} } grep m/^SSL_/, keys %params;
  10         61  
442             my $on_ssl_error = delete $params{on_ssl_error} or defined wantarray
443 5 50 66     57 or croak "Expected 'on_ssl_error'";
444              
445             my $f = $loop->listen(
446             socktype => 'stream',
447             %params,
448             )->on_done( sub {
449 5     5   19735 my $listener = shift;
450              
451 5         42 my $cleartext_acceptor = $listener->acceptor;
452             my $ssl_acceptor = sub {
453 5         49799 my $listener = shift;
454 5         27 my ( $listen_sock, %params ) = @_;
455 5         41 my $stream = $params{handle};
456 5 50 66     81 !defined $stream or $stream->isa( "IO::Async::Stream" ) or
457             croak "Can only accept SSL on IO::Async::Stream handles";
458              
459             $listener->$cleartext_acceptor( $listen_sock )->then( sub {
460 5         2142 my ( $socket ) = @_;
461              
462 5 50       24 return Future->done() unless $socket; # EAGAIN
463              
464 5 100       41 $stream->configure( handle => $socket ) if $stream;
465              
466             $loop->SSL_upgrade(
467             _SSL_args( SSL_server => 1, %ssl_params ),
468             handle => ( $stream || $socket ),
469             )->catch_with_f( ssl => sub {
470 1         92 my ( $f, $failure ) = @_;
471 1 50       6 if( $on_ssl_error ) {
472 1         5 $on_ssl_error->( $failure );
473 1         9 return Future->done; # eat it
474             }
475 0         0 return $f;
476 5   66     860 });
477 5         34 });
478 5         80 };
479              
480 5         38 $listener->configure( acceptor => $ssl_acceptor );
481 5         137 });
482              
483 5 100       72182 return $f if defined wantarray;
484              
485             # Caller is not going to keep hold of the Future, so we have to ensure it
486             # stays alive somehow
487 2     2   21 $f->on_ready( sub { undef $f } ); # intentional cycle
  2         98  
488             }
489              
490             =head1 STREAM PROTOCOL METHODS
491              
492             The following extra methods are added to L.
493              
494             =cut
495              
496             =head2 SSL_upgrade
497              
498             $protocol->SSL_upgrade( %params )->get;
499              
500             A shortcut to calling C<< $loop->SSL_upgrade >>. This method will unconfigure
501             the C of the Protocol, upgrade its underlying filehandle to SSL,
502             then reconfigure it again with SSL reader and writer functions on it. It takes
503             the same arguments as C<< $loop->SSL_upgrade >>, except that the C
504             argument is not required as it's taken from the Protocol's C.
505              
506             =cut
507              
508             sub IO::Async::Protocol::Stream::SSL_upgrade
509             {
510 2     2 0 6223 my $protocol = shift;
511 2         14 my %params = @_;
512              
513 2 50       11 my $on_upgraded = delete $params{on_upgraded} or croak "Expected 'on_upgraded'";
514              
515 2 50       17 my $loop = $protocol->get_loop or croak "Expected to be a member of a Loop";
516              
517 2         24 my $transport = $protocol->transport;
518              
519 2         24 $protocol->configure( transport => undef );
520              
521             $loop->SSL_upgrade(
522             handle => $transport,
523             on_upgraded => sub {
524 2     2   130 my ( $transport ) = @_;
525              
526 2         14 $protocol->configure( transport => $transport );
527              
528 2         986 $on_upgraded->();
529             },
530              
531 2         1207 %params,
532             );
533             }
534              
535             =head1 AUTHOR
536              
537             Paul Evans
538              
539             =cut
540              
541             0x55AA;