|
30 | 30 | import com.mongodb.connection.TopologyVersion; |
31 | 31 | import com.mongodb.lang.Nullable; |
32 | 32 |
|
| 33 | +import javax.net.ssl.SSLHandshakeException; |
| 34 | +import javax.net.ssl.SSLPeerUnverifiedException; |
| 35 | +import java.security.cert.CertPathValidatorException; |
| 36 | +import java.security.cert.CertificateException; |
33 | 37 | import java.util.Optional; |
34 | 38 |
|
35 | 39 | import static com.mongodb.assertions.Assertions.assertNotNull; |
@@ -162,6 +166,53 @@ boolean relatedToWriteConcern() { |
162 | 166 | return exception instanceof MongoWriteConcernWithResponseException; |
163 | 167 | } |
164 | 168 |
|
| 169 | + /** |
| 170 | + * Checks if the exception is related to TLS configuration errors that are NOT due to server overload. |
| 171 | + * These include certificate validation failures, protocol mismatches, etc. |
| 172 | + * |
| 173 | + * @return true if this is a TLS configuration error (not network-related) |
| 174 | + */ |
| 175 | + boolean relatedToTlsConfigurationError() { |
| 176 | + if (!(exception instanceof MongoSocketException)) { |
| 177 | + return false; |
| 178 | + } |
| 179 | + Throwable cause = exception.getCause(); |
| 180 | + while (cause != null) { |
| 181 | + // Check for various certificate validation and TLS configuration errors |
| 182 | + if (cause instanceof CertificateException |
| 183 | + || cause instanceof CertPathValidatorException |
| 184 | + || cause instanceof SSLPeerUnverifiedException) { |
| 185 | + return true; // Certificate/peer validation failure |
| 186 | + } |
| 187 | + |
| 188 | + // Check for SunCertPathBuilderException by class name to avoid compile-time dependency on internal classes |
| 189 | + String className = cause.getClass().getName(); |
| 190 | + if (className.equals("sun.security.provider.certpath.SunCertPathBuilderException")) { |
| 191 | + return true; // Certificate path building failure |
| 192 | + } |
| 193 | + |
| 194 | + // SSLHandshakeException can be either network or config, so we check the message |
| 195 | + if (cause instanceof SSLHandshakeException) { |
| 196 | + String message = cause.getMessage(); |
| 197 | + if (message != null) { |
| 198 | + String lowerMessage = message.toLowerCase(); |
| 199 | + // These indicate configuration issues, not network issues |
| 200 | + if (lowerMessage.contains("certificate") |
| 201 | + || lowerMessage.contains("verify") |
| 202 | + || lowerMessage.contains("trust") |
| 203 | + || lowerMessage.contains("hostname") |
| 204 | + || lowerMessage.contains("protocol") |
| 205 | + || lowerMessage.contains("cipher") |
| 206 | + || lowerMessage.contains("handshake_failure")) { |
| 207 | + return true; |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + cause = cause.getCause(); |
| 212 | + } |
| 213 | + return false; |
| 214 | + } |
| 215 | + |
165 | 216 | private static boolean stale(@Nullable final Throwable t, final ServerDescription currentServerDescription) { |
166 | 217 | return TopologyVersionHelper.topologyVersion(t) |
167 | 218 | .map(candidateTopologyVersion -> TopologyVersionHelper.newerOrEqual( |
|
0 commit comments