Add TLS to QuicIetfTransportErrorCodeString

This CL also logs the error code string from the interop test client to help debugging handshake failures.

gfe-relnote: n/a, log-only
PiperOrigin-RevId: 293340850
Change-Id: Ia8cca47421b366e33c5aadd7b717af49ec59c4fc
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index 351cd10..c0bdf1e 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -6,6 +6,7 @@
 
 #include <cstdint>
 
+#include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
 
@@ -79,8 +80,25 @@
     return #x;
 
 std::string QuicIetfTransportErrorCodeString(QuicIetfTransportErrorCodes c) {
-  if (static_cast<uint16_t>(c) >= 0xff00u) {
-    return quiche::QuicheStrCat("Private value: ", static_cast<uint16_t>(c));
+  if (static_cast<uint64_t>(c) >= 0xff00u) {
+    return quiche::QuicheStrCat("Private(", static_cast<uint64_t>(c), ")");
+  }
+  if (c >= CRYPTO_ERROR_FIRST && c <= CRYPTO_ERROR_LAST) {
+    const int tls_error = static_cast<int>(c - CRYPTO_ERROR_FIRST);
+    const char* tls_error_description = SSL_alert_desc_string_long(tls_error);
+    if (strcmp("unknown", tls_error_description) != 0) {
+      return quiche::QuicheStrCat("CRYPTO_ERROR(", tls_error_description, ")");
+    }
+    // SSL_alert_desc_string_long doesn't currently support these two errors.
+    // TODO(dschinazi) remove this once BoringSSL supports them.
+    // https://boringssl-review.googlesource.com/c/boringssl/+/39784
+    if (tls_error == SSL_AD_MISSING_EXTENSION) {
+      return "CRYPTO_ERROR(missing extension)";
+    }
+    if (tls_error == 120) {
+      return "CRYPTO_ERROR(no application protocol)";
+    }
+    return quiche::QuicheStrCat("CRYPTO_ERROR(unknown(", tls_error, "))");
   }
 
   switch (c) {
@@ -93,13 +111,19 @@
     RETURN_STRING_LITERAL(FINAL_SIZE_ERROR);
     RETURN_STRING_LITERAL(FRAME_ENCODING_ERROR);
     RETURN_STRING_LITERAL(TRANSPORT_PARAMETER_ERROR);
-    RETURN_STRING_LITERAL(VERSION_NEGOTIATION_ERROR);
+    RETURN_STRING_LITERAL(CONNECTION_ID_LIMIT_ERROR);
     RETURN_STRING_LITERAL(PROTOCOL_VIOLATION);
-    RETURN_STRING_LITERAL(INVALID_MIGRATION);
-    default:
-      return quiche::QuicheStrCat("Unknown Transport Error Code Value: ",
-                                  static_cast<uint16_t>(c));
+    RETURN_STRING_LITERAL(INVALID_TOKEN);
+    RETURN_STRING_LITERAL(CRYPTO_BUFFER_EXCEEDED);
+    // CRYPTO_ERROR is handled in the if before this switch, these cases do not
+    // change behavior and are only here to make the compiler happy.
+    case CRYPTO_ERROR_FIRST:
+    case CRYPTO_ERROR_LAST:
+      DCHECK(false) << "Unexpected error " << static_cast<uint64_t>(c);
+      break;
   }
+
+  return quiche::QuicheStrCat("Unknown(", static_cast<uint64_t>(c), ")");
 }
 
 std::ostream& operator<<(std::ostream& os,
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index f149ed3..e027b7b 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -562,9 +562,12 @@
   FINAL_SIZE_ERROR = 0x6,
   FRAME_ENCODING_ERROR = 0x7,
   TRANSPORT_PARAMETER_ERROR = 0x8,
-  VERSION_NEGOTIATION_ERROR = 0x9,
+  CONNECTION_ID_LIMIT_ERROR = 0x9,
   PROTOCOL_VIOLATION = 0xA,
-  INVALID_MIGRATION = 0xC,
+  INVALID_TOKEN = 0xB,
+  CRYPTO_BUFFER_EXCEEDED = 0xD,
+  CRYPTO_ERROR_FIRST = 0x100,
+  CRYPTO_ERROR_LAST = 0x1FF,
 };
 QUIC_EXPORT_PRIVATE std::string QuicIetfTransportErrorCodeString(
     QuicIetfTransportErrorCodes c);
diff --git a/quic/core/quic_types_test.cc b/quic/core/quic_types_test.cc
index d238c78..63ebdb0 100644
--- a/quic/core/quic_types_test.cc
+++ b/quic/core/quic_types_test.cc
@@ -6,6 +6,7 @@
 
 #include <cstdint>
 
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h"
 
@@ -17,7 +18,7 @@
 
 TEST_F(QuicTypesTest, QuicIetfTransportErrorCodeString) {
   // QuicIetfTransportErrorCode out of bound.
-  for (quic::QuicErrorCode error = quic::QUIC_ENCRYPTION_FAILURE;
+  for (quic::QuicErrorCode error = quic::QUIC_PACKET_TOO_LARGE;
        error < quic::QUIC_LAST_ERROR;
        error = static_cast<quic::QuicErrorCode>(error + 1)) {
     QuicErrorCodeToIetfMapping mapping =
@@ -25,8 +26,10 @@
     if (mapping.is_transport_close_) {
       EXPECT_EQ(QuicIetfTransportErrorCodeString(mapping.transport_error_code_),
                 quiche::QuicheStrCat(
-                    "Unknown Transport Error Code Value: ",
-                    static_cast<uint16_t>(mapping.transport_error_code_)));
+                    "Unknown(",
+                    static_cast<uint64_t>(mapping.transport_error_code_), ")"))
+          << " " << static_cast<uint64_t>(error) << " "
+          << QuicErrorCodeToString(error);
     }
   }
 }
diff --git a/quic/tools/quic_client_interop_test_bin.cc b/quic/tools/quic_client_interop_test_bin.cc
index 9742d63..03d32a5 100644
--- a/quic/tools/quic_client_interop_test_bin.cc
+++ b/quic/tools/quic_client_interop_test_bin.cc
@@ -106,11 +106,18 @@
       case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
         if (frame.transport_error_code == NO_IETF_QUIC_ERROR) {
           InsertFeature(Feature::kConnectionClose);
+        } else {
+          QUIC_LOG(ERROR) << "Received transport connection close "
+                          << QuicIetfTransportErrorCodeString(
+                                 frame.transport_error_code);
         }
         break;
       case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
         if (frame.application_error_code == 0) {
           InsertFeature(Feature::kConnectionClose);
+        } else {
+          QUIC_LOG(ERROR) << "Received application connection close "
+                          << frame.application_error_code;
         }
         break;
     }