Only retransmit on Finished if frag_off == 0.

If the peer fragments Finished into multiple pieces, there is no need to
retransmit multiple times.

Change-Id: Ibf708ad079e1633afd420ff1c9be88a80020cba9
Reviewed-on: https://boringssl-review.googlesource.com/3762
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c
index 8cbfe94..a5631f7 100644
--- a/ssl/d1_pkt.c
+++ b/ssl/d1_pkt.c
@@ -840,14 +840,19 @@
     struct hm_header_st msg_hdr;
     dtls1_get_message_header(&rr->data[rr->off], &msg_hdr);
 
-    /* Ignore the Finished, but retransmit our last flight of messages. If the
-     * peer sends the second Finished, they may not have received ours. */
+    /* Ignore a stray Finished from the previous handshake. */
     if (msg_hdr.type == SSL3_MT_FINISHED) {
-      if (dtls1_check_timeout_num(s) < 0) {
-        return -1;
+      if (msg_hdr.frag_off == 0) {
+        /* Retransmit our last flight of messages. If the peer sends the second
+         * Finished, they may not have received ours. Only do this for the
+         * first fragment, in case the Finished was fragmented. */
+        if (dtls1_check_timeout_num(s) < 0) {
+          return -1;
+        }
+
+        dtls1_retransmit_buffered_messages(s);
       }
 
-      dtls1_retransmit_buffered_messages(s);
       rr->length = 0;
       goto start;
     }
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 69160b3..17badb0 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2846,6 +2846,21 @@
 		resumeSession: true,
 		flags:         []string{"-async"},
 	})
+
+	// Test that the final Finished retransmitting isn't
+	// duplicated if the peer badly fragments everything.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		protocol: dtls,
+		name:     "DTLS-Retransmit-Fragmented",
+		config: Config{
+			Bugs: ProtocolBugs{
+				TimeoutSchedule:          []time.Duration{timeouts[0]},
+				MaxHandshakeRecordLength: 2,
+			},
+		},
+		flags: []string{"-async"},
+	})
 }
 
 func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {