Add tests for bidirectional shutdown.

Now that it even works at all (type = 0 bug aside), add tests for it.
Test both close_notify being received before and after SSL_shutdown is
called. In the latter case, have the peer send some junk to be ignored
to test that works.

Also test that SSL_shutdown fails on unclean shutdown and that quiet
shutdowns ignore it.

BUG=526437

Change-Id: Iff13b08feb03e82f21ecab0c66d5f85aec256137
Reviewed-on: https://boringssl-review.googlesource.com/5769
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 950c02a..7ada5f1 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -191,6 +191,10 @@
 	// shimWritesFirst controls whether the shim sends an initial "hello"
 	// message before doing a roundtrip with the runner.
 	shimWritesFirst bool
+	// shimShutsDown, if true, runs a test where the shim shuts down the
+	// connection immediately after the handshake rather than echoing
+	// messages from the runner.
+	shimShutsDown bool
 	// renegotiate indicates the the connection should be renegotiated
 	// during the exchange.
 	renegotiate bool
@@ -270,6 +274,7 @@
 			tlsConn = Client(conn, config)
 		}
 	}
+	defer tlsConn.Close()
 
 	if err := tlsConn.Handshake(); err != nil {
 		return err
@@ -420,6 +425,11 @@
 			tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage)
 		}
 
+		if test.shimShutsDown {
+			// The shim will not respond.
+			continue
+		}
+
 		buf := make([]byte, len(testMessage))
 		if test.protocol == dtls {
 			bufTmp := make([]byte, len(buf)+1)
@@ -547,6 +557,10 @@
 		flags = append(flags, "-shim-writes-first")
 	}
 
+	if test.shimShutsDown {
+		flags = append(flags, "-shim-shuts-down")
+	}
+
 	if test.exportKeyingMaterial > 0 {
 		flags = append(flags, "-export-keying-material", strconv.Itoa(test.exportKeyingMaterial))
 		flags = append(flags, "-export-label", test.exportLabel)
@@ -1847,8 +1861,29 @@
 			noSessionCache: true,
 			flags:          []string{"-expect-no-session"},
 		},
+		{
+			name: "Unclean-Shutdown",
+			config: Config{
+				Bugs: ProtocolBugs{
+					NoCloseNotify:     true,
+					ExpectCloseNotify: true,
+				},
+			},
+			shimShutsDown: true,
+			flags:         []string{"-check-close-notify"},
+			shouldFail:    true,
+			expectedError: "Unexpected SSL_shutdown result: -1 != 1",
+		},
+		{
+			name: "Unclean-Shutdown-Ignored",
+			config: Config{
+				Bugs: ProtocolBugs{
+					NoCloseNotify: true,
+				},
+			},
+			shimShutsDown: true,
+		},
 	}
-
 	testCases = append(testCases, basicTests...)
 }
 
@@ -2561,6 +2596,33 @@
 			resumeSession:   true,
 			expectChannelID: true,
 		})
+
+		// Bidirectional shutdown with the runner initiating.
+		tests = append(tests, testCase{
+			name: "Shutdown-Runner",
+			config: Config{
+				Bugs: ProtocolBugs{
+					ExpectCloseNotify: true,
+				},
+			},
+			flags: []string{"-check-close-notify"},
+		})
+
+		// Bidirectional shutdown with the shim initiating. The runner,
+		// in the meantime, sends garbage before the close_notify which
+		// the shim must ignore.
+		tests = append(tests, testCase{
+			name: "Shutdown-Shim",
+			config: Config{
+				Bugs: ProtocolBugs{
+					ExpectCloseNotify: true,
+				},
+			},
+			shimShutsDown:     true,
+			sendEmptyRecords:  1,
+			sendWarningAlerts: 1,
+			flags:             []string{"-check-close-notify"},
+		})
 	} else {
 		tests = append(tests, testCase{
 			name: "SkipHelloVerifyRequest",