Add tests for SSL_peek.

SSL_peek works fine for us, but OpenSSL 1.1.0 regressed this
(https://github.com/openssl/openssl/issues/1563), and we don't have
tests either. Fix this.

SSL_peek can handle all weird events that SSL_read can, so use runner
and tell bssl_shim to do a SSL_peek + SSL_peek + SSL_read instead of
SSL_read. Then add tests for all the events we may discover.

Change-Id: I9e8635e3ca19653a02a883f220ab1332d4412f98
Reviewed-on: https://boringssl-review.googlesource.com/11090
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index dc6e99d..533e7b2 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1020,11 +1020,33 @@
       // trigger a retransmit, so disconnect the write quota.
       AsyncBioEnforceWriteQuota(test_state->async_bio, false);
     }
-    ret = SSL_read(ssl, out, max_out);
+    ret = config->peek_then_read ? SSL_peek(ssl, out, max_out)
+                                 : SSL_read(ssl, out, max_out);
     if (config->async) {
       AsyncBioEnforceWriteQuota(test_state->async_bio, true);
     }
   } while (config->async && RetryAsync(ssl, ret));
+
+  if (config->peek_then_read && ret > 0) {
+    std::unique_ptr<uint8_t[]> buf(new uint8_t[static_cast<size_t>(ret)]);
+
+    // SSL_peek should synchronously return the same data.
+    int ret2 = SSL_peek(ssl, buf.get(), ret);
+    if (ret2 != ret ||
+        memcmp(buf.get(), out, ret) != 0) {
+      fprintf(stderr, "First and second SSL_peek did not match.\n");
+      return -1;
+    }
+
+    // SSL_read should synchronously return the same data and consume it.
+    ret2 = SSL_read(ssl, buf.get(), ret);
+    if (ret2 != ret ||
+        memcmp(buf.get(), out, ret) != 0) {
+      fprintf(stderr, "SSL_peek and SSL_read did not match.\n");
+      return -1;
+    }
+  }
+
   return ret;
 }
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index c350ac5..01de14f 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -8192,6 +8192,77 @@
 	})
 }
 
+func addPeekTests() {
+	// Test SSL_peek works, including on empty records.
+	testCases = append(testCases, testCase{
+		name:             "Peek-Basic",
+		sendEmptyRecords: 1,
+		flags:            []string{"-peek-then-read"},
+	})
+
+	// Test SSL_peek can drive the initial handshake.
+	testCases = append(testCases, testCase{
+		name: "Peek-ImplicitHandshake",
+		flags: []string{
+			"-peek-then-read",
+			"-implicit-handshake",
+		},
+	})
+
+	// Test SSL_peek can discover and drive a renegotiation.
+	testCases = append(testCases, testCase{
+		name: "Peek-Renegotiate",
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		renegotiate: 1,
+		flags: []string{
+			"-peek-then-read",
+			"-renegotiate-freely",
+			"-expect-total-renegotiations", "1",
+		},
+	})
+
+	// Test SSL_peek can discover a close_notify.
+	testCases = append(testCases, testCase{
+		name: "Peek-Shutdown",
+		config: Config{
+			Bugs: ProtocolBugs{
+				ExpectCloseNotify: true,
+			},
+		},
+		flags: []string{
+			"-peek-then-read",
+			"-check-close-notify",
+		},
+	})
+
+	// Test SSL_peek can discover an alert.
+	testCases = append(testCases, testCase{
+		name: "Peek-Alert",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendSpuriousAlert: alertRecordOverflow,
+			},
+		},
+		flags:         []string{"-peek-then-read"},
+		shouldFail:    true,
+		expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+	})
+
+	// Test SSL_peek can handle KeyUpdate.
+	testCases = append(testCases, testCase{
+		name: "Peek-KeyUpdate",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendKeyUpdateBeforeEveryAppDataRecord: true,
+			},
+		},
+		flags: []string{"-peek-then-read"},
+	})
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -8308,6 +8379,7 @@
 	addWrongMessageTypeTests()
 	addTrailingMessageDataTests()
 	addTLS13HandshakeTests()
+	addPeekTests()
 
 	var wg sync.WaitGroup
 
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 1f01166..b7dc511 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -104,6 +104,7 @@
     &TestConfig::use_old_client_cert_callback },
   { "-use-null-client-ca-list", &TestConfig::use_null_client_ca_list },
   { "-send-alert", &TestConfig::send_alert },
+  { "-peek-then-read", &TestConfig::peek_then_read },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 93c22ce..e0029a5 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -112,6 +112,7 @@
   int initial_timeout_duration_ms = 0;
   bool use_null_client_ca_list = false;
   bool send_alert = false;
+  bool peek_then_read = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);