diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java b/java/org/apache/tomcat/util/net/SecureNio2Channel.java index 92b5f3d5c494..0437edc30a72 100644 --- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java +++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java @@ -423,7 +423,7 @@ protected int processSNI() throws IOException { break; case NON_SECURE: netOutBuffer.clear(); - netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); + netOutBuffer.put(extractor.getUseTlsResponse()); netOutBuffer.flip(); flush(); throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java b/java/org/apache/tomcat/util/net/SecureNioChannel.java index 768ec458f175..e2c7985a920e 100644 --- a/java/org/apache/tomcat/util/net/SecureNioChannel.java +++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java @@ -293,7 +293,7 @@ protected int processSNI() throws IOException { break; case NON_SECURE: netOutBuffer.clear(); - netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); + netOutBuffer.put(extractor.getUseTlsResponse()); netOutBuffer.flip(); flushOutbound(); throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); diff --git a/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java b/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java index 28d3358510cc..de233124f4e9 100644 --- a/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java +++ b/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java @@ -40,6 +40,7 @@ public class TLSClientHelloExtractor { private static final Log log = LogFactory.getLog(TLSClientHelloExtractor.class); private static final StringManager sm = StringManager.getManager(TLSClientHelloExtractor.class); + private final ByteBuffer netInBuffer; private final ExtractorResult result; private final List clientRequestedCiphers; private final List clientRequestedCipherNames; @@ -60,6 +61,7 @@ public class TLSClientHelloExtractor { "Bad Request\r\n" + "This combination of host and port requires TLS.\r\n").getBytes(StandardCharsets.UTF_8); + public static ResponseCustomizer RESPONSE_CUSTOMIZER = new ResponseCustomizer() {}; /** * Creates the instance of the parser and processes the provided buffer. The @@ -71,6 +73,7 @@ public class TLSClientHelloExtractor { * @throws IOException If the client hello message is malformed */ public TLSClientHelloExtractor(ByteBuffer netInBuffer) throws IOException { + this.netInBuffer = netInBuffer; // Buffer is in write mode at this point. Record the current position so // the buffer state can be restored at the end of this method. int pos = netInBuffer.position(); @@ -245,6 +248,20 @@ public List getClientRequestedProtocols() { } } + public byte[] getUseTlsResponse() { + // Buffer is in write mode at this point. Record the current position so + // the buffer state can be restored at the end of this method. + int pos = netInBuffer.position(); + int limit = netInBuffer.limit(); + try { + netInBuffer.flip(); + return RESPONSE_CUSTOMIZER.customize(netInBuffer); + } finally { + // Whatever happens, return the buffer to its original state + netInBuffer.limit(limit); + netInBuffer.position(pos); + } + } private static ExtractorResult handleIncompleteRead(ByteBuffer bb) { if (bb.limit() == bb.capacity()) { @@ -433,7 +450,6 @@ private static void readSupportedVersions(ByteBuffer bb, List protocolNa } } - public enum ExtractorResult { COMPLETE, NOT_PRESENT, @@ -441,4 +457,25 @@ public enum ExtractorResult { NEED_READ, NON_SECURE } + + /** + * Allows the HTTP response to be changed. + *
+     * {@code
+     * TLSClientHelloExtractor.RESPONSE_CUSTOMIZER = new ResponseCustomizer() {
+     *     @Override
+     *     public byte[] customize(ByteBuffer netInBuffer) {
+     *         return ("HTTP/1.1 302 \r\n" +
+     *             "Location: https://www.example.org/index.html\r\n" +
+     *             "\r\n").getBytes(StandardCharsets.UTF_8);
+     *     }
+     * };
+     * }
+     * 
+ */ + public interface ResponseCustomizer { + default byte[] customize(ByteBuffer netInBuffer) { + return TLSClientHelloExtractor.USE_TLS_RESPONSE; + } + } }