Add a unit test for one-sided shutdown.

OpenSSL was actually super-buggy here (though known bugs on our end have been
fixed), but pyOpenSSL was confused and incorrectly documented that callers call
SSL_read after SSL_shutdown to do bidi shutdown, so we should probably support
this. Add a test that it works.

Change-Id: I2b6d012161330aeb4cf894bae3a0b6a55d53c70d
Reviewed-on: https://boringssl-review.googlesource.com/8093
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index aadc4f0..ef38902 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1079,23 +1079,9 @@
       PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
 }
 
-static bool TestSequenceNumber(bool dtls) {
-  ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
-  ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
-  if (!client_ctx || !server_ctx) {
-    return false;
-  }
-
-  ScopedX509 cert = GetTestCertificate();
-  ScopedEVP_PKEY key = GetTestKey();
-  if (!cert || !key ||
-      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
-    return false;
-  }
-
-  // Create a client and server connected to each other.
-  ScopedSSL client(SSL_new(client_ctx.get())), server(SSL_new(server_ctx.get()));
+static bool ConnectClientAndServer(ScopedSSL *out_client, ScopedSSL *out_server,
+                                   SSL_CTX *client_ctx, SSL_CTX *server_ctx) {
+  ScopedSSL client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
   if (!client || !server) {
     return false;
   }
@@ -1135,6 +1121,32 @@
     }
   }
 
+  *out_client = std::move(client);
+  *out_server = std::move(server);
+  return true;
+}
+
+static bool TestSequenceNumber(bool dtls) {
+  ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+  ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+  if (!client_ctx || !server_ctx) {
+    return false;
+  }
+
+  ScopedX509 cert = GetTestCertificate();
+  ScopedEVP_PKEY key = GetTestKey();
+  if (!cert || !key ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+    return false;
+  }
+
+  ScopedSSL client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get())) {
+    return false;
+  }
+
   uint64_t client_read_seq = SSL_get_read_sequence(client.get());
   uint64_t client_write_seq = SSL_get_write_sequence(client.get());
   uint64_t server_read_seq = SSL_get_read_sequence(server.get());
@@ -1183,6 +1195,62 @@
   return true;
 }
 
+static bool TestOneSidedShutdown() {
+  ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
+  ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
+  if (!client_ctx || !server_ctx) {
+    return false;
+  }
+
+  ScopedX509 cert = GetTestCertificate();
+  ScopedEVP_PKEY key = GetTestKey();
+  if (!cert || !key ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+    return false;
+  }
+
+  ScopedSSL client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get())) {
+    return false;
+  }
+
+  // Shut down half the connection. SSL_shutdown will return 0 to signal only
+  // one side has shut down.
+  if (SSL_shutdown(client.get()) != 0) {
+    fprintf(stderr, "Could not shutdown.\n");
+    return false;
+  }
+
+  // Reading from the server should consume the EOF.
+  uint8_t byte;
+  if (SSL_read(server.get(), &byte, 1) != 0 ||
+      SSL_get_error(server.get(), 0) != SSL_ERROR_ZERO_RETURN) {
+    fprintf(stderr, "Connection was not shut down cleanly.\n");
+    return false;
+  }
+
+  // However, the server may continue to write data and then shut down the
+  // connection.
+  byte = 42;
+  if (SSL_write(server.get(), &byte, 1) != 1 ||
+      SSL_read(client.get(), &byte, 1) != 1 ||
+      byte != 42) {
+    fprintf(stderr, "Could not send byte.\n");
+    return false;
+  }
+
+  // The server may then shutdown the connection.
+  if (SSL_shutdown(server.get()) != 1 ||
+      SSL_shutdown(client.get()) != 1) {
+    fprintf(stderr, "Could not complete shutdown.\n");
+    return false;
+  }
+
+  return true;
+}
+
 int main() {
   CRYPTO_library_init();
 
@@ -1206,7 +1274,8 @@
       !TestClientCAList() ||
       !TestInternalSessionCache() ||
       !TestSequenceNumber(false /* TLS */) ||
-      !TestSequenceNumber(true /* DTLS */)) {
+      !TestSequenceNumber(true /* DTLS */) ||
+      !TestOneSidedShutdown()) {
     ERR_print_errors_fp(stderr);
     return 1;
   }