d608c17
From e0b42b0120b941b5675e4071445424dc8a1230e1 Mon Sep 17 00:00:00 2001
d608c17
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
d608c17
Date: Wed, 15 Aug 2018 14:46:52 +0200
d608c17
Subject: [PATCH] Move SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITE retry from
d608c17
 read()/write() up
d608c17
MIME-Version: 1.0
d608c17
Content-Type: text/plain; charset=UTF-8
d608c17
Content-Transfer-Encoding: 8bit
d608c17
d608c17
Original OpenSSL 1.1.1 fix broke IO-Socket-SSL-2.058's t/core.t test
d608c17
because it tests non-blocking socket operations and expects to see
d608c17
SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITE errors and to handle them
d608c17
byt itself.
d608c17
d608c17
This patch purifies Net::SSLeay::{read,write}() to behave exactly as
d608c17
underlying OpenSSL functions. The retry is moved to
d608c17
Net::SSLeay::ssl_read_all. All relevant Net::SSLeay::{read,write}() calls in
d608c17
tests are changed into Net::SSLea::ssl_{read,write}_all().
d608c17
d608c17
All applications should implement the retry themsleves or use
d608c17
ssl_*_all() instead.
d608c17
d608c17
Signed-off-by: Petr Písař <ppisar@redhat.com>
d608c17
---
d608c17
 SSLeay.xs            | 28 +++++++---------------------
d608c17
 lib/Net/SSLeay.pm    | 22 +++++++++++++++-------
d608c17
 t/local/07_sslecho.t | 12 ++++++------
d608c17
 t/local/36_verify.t  |  9 +++++----
d608c17
 4 files changed, 33 insertions(+), 38 deletions(-)
d608c17
d608c17
diff --git a/SSLeay.xs b/SSLeay.xs
d608c17
index 5aed4d7..7cb6eab 100644
d608c17
--- a/SSLeay.xs
d608c17
+++ b/SSLeay.xs
d608c17
@@ -1997,19 +1997,13 @@ SSL_read(s,max=32768)
d608c17
     PREINIT:
d608c17
 	char *buf;
d608c17
 	int got;
d608c17
+	int succeeded = 1;
d608c17
     PPCODE:
d608c17
 	New(0, buf, max, char);
d608c17
 
d608c17
-	do {
d608c17
-		int err;
d608c17
-
d608c17
-		got = SSL_read(s, buf, max);
d608c17
-		if (got > 0)
d608c17
-			break;
d608c17
-		err = SSL_get_error(s, got);
d608c17
-		if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
d608c17
-			break;
d608c17
-	} while (1);
d608c17
+	got = SSL_read(s, buf, max);
d608c17
+	if (got <= 0 && SSL_ERROR_ZERO_RETURN != SSL_get_error(s, got))
d608c17
+	       succeeded = 0;
d608c17
 
d608c17
 	/* If in list context, return 2-item list:
d608c17
 	 *   first return value:  data gotten, or undef on error (got<0)
d608c17
@@ -2017,13 +2011,13 @@ SSL_read(s,max=32768)
d608c17
 	 */
d608c17
 	if (GIMME_V==G_ARRAY) {
d608c17
 	    EXTEND(SP, 2);
d608c17
-	    PUSHs(sv_2mortal(got>=0 ? newSVpvn(buf, got) : newSV(0)));
d608c17
+	    PUSHs(sv_2mortal(succeeded ? newSVpvn(buf, got) : newSV(0)));
d608c17
 	    PUSHs(sv_2mortal(newSViv(got)));
d608c17
 
d608c17
 	/* If in scalar or void context, return data gotten, or undef on error. */
d608c17
 	} else {
d608c17
 	    EXTEND(SP, 1);
d608c17
-	    PUSHs(sv_2mortal(got>=0 ? newSVpvn(buf, got) : newSV(0)));
d608c17
+	    PUSHs(sv_2mortal(succeeded ? newSVpvn(buf, got) : newSV(0)));
d608c17
 	}
d608c17
 
d608c17
 	Safefree(buf);
d608c17
@@ -2066,15 +2060,7 @@ SSL_write(s,buf)
d608c17
      INPUT:
d608c17
      char *  buf = SvPV( ST(1), len);
d608c17
      CODE:
d608c17
-     do {
d608c17
-	     ret = SSL_write (s, buf, (int)len);
d608c17
-	     if (ret > 0)
d608c17
-		     break;
d608c17
-	     err = SSL_get_error(s, ret);
d608c17
-	     if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
d608c17
-		     break;
d608c17
-     } while (1);
d608c17
-     RETVAL = ret;
d608c17
+     RETVAL = SSL_write (s, buf, (int)len);
d608c17
      OUTPUT:
d608c17
      RETVAL
d608c17
 
d608c17
diff --git a/lib/Net/SSLeay.pm b/lib/Net/SSLeay.pm
d608c17
index 3adf12c..afc6c8f 100644
d608c17
--- a/lib/Net/SSLeay.pm
d608c17
+++ b/lib/Net/SSLeay.pm
d608c17
@@ -579,14 +579,22 @@ sub debug_read {
d608c17
 sub ssl_read_all {
d608c17
     my ($ssl,$how_much) = @_;
d608c17
     $how_much = 2000000000 unless $how_much;
d608c17
-    my ($got, $errs);
d608c17
+    my ($got, $rv, $errs);
d608c17
     my $reply = '';
d608c17
 
d608c17
     while ($how_much > 0) {
d608c17
-        $got = Net::SSLeay::read($ssl,
d608c17
+        ($got, $rv) = Net::SSLeay::read($ssl,
d608c17
                 ($how_much > 32768) ? 32768 : $how_much
d608c17
         );
d608c17
-        last if $errs = print_errs('SSL_read');
d608c17
+        if (! defined $got) {
d608c17
+            my $err = Net::SSLeay::get_error($ssl, $rv);
d608c17
+            if ($err != Net::SSLeay::ERROR_WANT_READ() and
d608c17
+                    $err != Net::SSLeay::ERROR_WANT_WRITE()) {
d608c17
+                $errs = print_errs('SSL_read');
d608c17
+                last;
d608c17
+            }
d608c17
+            next;
d608c17
+        }
d608c17
         $how_much -= blength($got);
d608c17
         debug_read(\$reply, \$got) if $trace>1;
d608c17
         last if $got eq '';  # EOF
d608c17
@@ -839,14 +847,14 @@ sub ssl_read_until ($;$$) {
d608c17
 	    $found = index($match, $delim);
d608c17
 
d608c17
 	    if ($found > -1) {
d608c17
-		#$got = Net::SSLeay::read($ssl, $found+$len_delim);
d608c17
+		#$got = Net::SSLeay::ssl_read_all($ssl, $found+$len_delim);
d608c17
 		#read up to the end of the delimiter
d608c17
-		$got = Net::SSLeay::read($ssl,
d608c17
+		$got = Net::SSLeay::ssl_read_all($ssl,
d608c17
 					 $found + $len_delim
d608c17
 					 - ((blength($match)) - (blength($got))));
d608c17
 		$done = 1;
d608c17
 	    } else {
d608c17
-		$got = Net::SSLeay::read($ssl, $peek_length);
d608c17
+		$got = Net::SSLeay::ssl_read_all($ssl, $peek_length);
d608c17
 		$done = 1 if ($peek_length == $max_length - blength($reply));
d608c17
 	    }
d608c17
 
d608c17
@@ -857,7 +865,7 @@ sub ssl_read_until ($;$$) {
d608c17
 	}
d608c17
     } else {
d608c17
 	while (!defined $max_length || length $reply < $max_length) {
d608c17
-	    $got = Net::SSLeay::read($ssl,1);  # one by one
d608c17
+	    $got = Net::SSLeay::ssl_read_all($ssl,1);  # one by one
d608c17
 	    last if print_errs('SSL_read');
d608c17
 	    debug_read(\$reply, \$got) if $trace>1;
d608c17
 	    last if $got eq '';
d608c17
diff --git a/t/local/07_sslecho.t b/t/local/07_sslecho.t
d608c17
index 74e317a..7f19027 100644
d608c17
--- a/t/local/07_sslecho.t
d608c17
+++ b/t/local/07_sslecho.t
d608c17
@@ -134,10 +134,10 @@ my @results;
d608c17
 
d608c17
     push @results, [ Net::SSLeay::get_cipher($ssl), 'get_cipher' ];
d608c17
 
d608c17
-    push @results, [ Net::SSLeay::write($ssl, $msg), 'write' ];
d608c17
+    push @results, [ Net::SSLeay::ssl_write_all($ssl, $msg), 'write' ];
d608c17
     shutdown($s, 1);
d608c17
 
d608c17
-    my ($got) = Net::SSLeay::read($ssl);
d608c17
+    my $got = Net::SSLeay::ssl_read_all($ssl);
d608c17
     push @results, [ $got eq uc($msg), 'read' ];
d608c17
 
d608c17
     Net::SSLeay::free($ssl);
d608c17
@@ -177,7 +177,7 @@ my @results;
d608c17
             Net::SSLeay::set_fd($ssl, fileno($s));
d608c17
             Net::SSLeay::connect($ssl);
d608c17
 
d608c17
-            Net::SSLeay::write($ssl, $msg);
d608c17
+            Net::SSLeay::ssl_write_all($ssl, $msg);
d608c17
 
d608c17
             shutdown $s, 2;
d608c17
             close $s;
d608c17
@@ -231,15 +231,15 @@ my @results;
d608c17
             Net::SSLeay::set_fd($ssl3, $s3);
d608c17
 
d608c17
             Net::SSLeay::connect($ssl1);
d608c17
-            Net::SSLeay::write($ssl1, $msg);
d608c17
+            Net::SSLeay::ssl_write_all($ssl1, $msg);
d608c17
             shutdown $s1, 2;
d608c17
 
d608c17
             Net::SSLeay::connect($ssl2);
d608c17
-            Net::SSLeay::write($ssl2, $msg);
d608c17
+            Net::SSLeay::ssl_write_all($ssl2, $msg);
d608c17
             shutdown $s2, 2;
d608c17
 
d608c17
             Net::SSLeay::connect($ssl3);
d608c17
-            Net::SSLeay::write($ssl3, $msg);
d608c17
+            Net::SSLeay::ssl_write_all($ssl3, $msg);
d608c17
             shutdown $s3, 2;
d608c17
 
d608c17
             close $s1;
d608c17
diff --git a/t/local/36_verify.t b/t/local/36_verify.t
d608c17
index 2837288..b04be13 100644
d608c17
--- a/t/local/36_verify.t
d608c17
+++ b/t/local/36_verify.t
d608c17
@@ -252,8 +252,9 @@ sub client {
d608c17
     Net::SSLeay::set_fd($ssl, $cl);
d608c17
     Net::SSLeay::connect($ssl);
d608c17
     my $end = "end";
d608c17
-    Net::SSLeay::write($ssl, $end);
d608c17
-    ok($end eq Net::SSLeay::read($ssl),  'Successful termination');
d608c17
+    Net::SSLeay::ssl_write_all($ssl, $end);
d608c17
+    Net::SSLeay::shutdown($ssl);
d608c17
+    ok($end eq Net::SSLeay::ssl_read_all($ssl),  'Successful termination');
d608c17
     return;
d608c17
 }
d608c17
 
d608c17
@@ -291,10 +292,10 @@ sub run_server
d608c17
 	next unless $ret == 1;
d608c17
 
d608c17
 	# Termination request or other message from client
d608c17
-	my $msg = Net::SSLeay::read($ssl);
d608c17
+	my $msg = Net::SSLeay::ssl_read_all($ssl);
d608c17
 	if (defined $msg and $msg eq 'end')
d608c17
 	{
d608c17
-	    Net::SSLeay::write($ssl, 'end');
d608c17
+	    Net::SSLeay::ssl_write_all($ssl, 'end');
d608c17
 	    exit (0);
d608c17
 	}
d608c17
     }
d608c17
-- 
d608c17
2.14.4
d608c17