You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/04/10 14:36:32 UTC
svn commit: r1672631 - in /tomcat/trunk/java/org/apache/tomcat/util/net:
LocalStrings.properties SNIExtractor.java SecureNioChannel.java
Author: markt
Date: Fri Apr 10 12:36:31 2015
New Revision: 1672631
URL: http://svn.apache.org/r1672631
Log:
SNI basically working with NIO.
Debug code still present. Needs to be made more robust.
Modified:
tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties
tomcat/trunk/java/org/apache/tomcat/util/net/SNIExtractor.java
tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties?rev=1672631&r1=1672630&r2=1672631&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/LocalStrings.properties Fri Apr 10 12:36:31 2015
@@ -97,6 +97,8 @@ channel.nio.ssl.invalidBuffer=You can on
channel.nio.ssl.expandNetInBuffer=Expanding network input buffer to [{0}] bytes
channel.nio.ssl.expandNetOutBuffer=Expanding network input buffer to [{0}] bytes
+sniExtractor.clientHelloTooBig=The ClientHello was not presented in a single TLS record so no SNI information could be extracted
+
socket.closed=The socket associated with this connection has been closed.
socket.apr.clientAbort=The client aborted the connection.
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SNIExtractor.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SNIExtractor.java?rev=1672631&r1=1672630&r2=1672631&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/SNIExtractor.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/SNIExtractor.java Fri Apr 10 12:36:31 2015
@@ -17,9 +17,17 @@
package org.apache.tomcat.util.net;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
public class SNIExtractor {
+ private static final Log log = LogFactory.getLog(SNIExtractor.class);
+ private static final StringManager sm = StringManager.getManager(SNIExtractor.class);
+
private final SNIResult result;
private final String sniValue;
@@ -41,18 +49,60 @@ public class SNIExtractor {
return;
}
- if (!isTLSClientHello(netInBuffer)) {
+ if (!isTLSHandshake(netInBuffer)) {
return;
}
- if (!isAllRecordPresent(netInBuffer)) {
- result = SNIResult.UNDERFLOW;
+ int recordSizeToRead = recordSizeToRead(netInBuffer);
+ if (recordSizeToRead == -1) {
+ // Not enough data in the buffer for the full record
+ if (netInBuffer.limit() == netInBuffer.capacity()) {
+ // Buffer too small
+ result = SNIResult.UNDERFLOW;
+ return;
+ } else {
+ // Need to read more data
+ result = SNIResult.NEED_READ;
+ return;
+ }
+
+ }
+
+ if (!isClientHello(netInBuffer)) {
return;
}
- System.out.println("Looking good so far to find some SNI data");
- // TODO Parse the remainder of the data
+ int clientHelloSizeToRead = clientHelloSize(netInBuffer);
+ if (clientHelloSizeToRead == -1) {
+ // Client hello can't have fitted into single TLS record.
+ // Treat this as not present.
+ log.warn(sm.getString("sniExtractor.clientHelloTooBig"));
+ return;
+ }
+
+ // Protocol Version (2 bytes)
+ netInBuffer.getChar();
+ swallowRandom(netInBuffer);
+ // Session ID
+ swallowUnit8Vector(netInBuffer);
+ swallowCipherSuites(netInBuffer);
+ // Compression methods
+ swallowUnit8Vector(netInBuffer);
+
+ if (!netInBuffer.hasRemaining()) {
+ // No more data means no extensions present
+ return;
+ }
+ // Extension length
+ netInBuffer.getChar();
+ // Read th eextensions until we run out of data or find the SNI
+ while (netInBuffer.hasRemaining() && sniValue == null) {
+ sniValue = readSniExtension(netInBuffer);
+ }
+ if (sniValue != null) {
+ result = SNIResult.FOUND;
+ }
} finally {
this.result = result;
this.sniValue = sniValue;
@@ -85,7 +135,7 @@ public class SNIExtractor {
}
- private static boolean isTLSClientHello(ByteBuffer bb) {
+ private static boolean isTLSHandshake(ByteBuffer bb) {
// For a TLS client hello the first byte must be 22 - handshake
if (bb.get() != 22) {
return false;
@@ -100,19 +150,86 @@ public class SNIExtractor {
}
- private static boolean isAllRecordPresent(ByteBuffer bb) {
+ private static int recordSizeToRead(ByteBuffer bb) {
// Next two bytes (unsigned) are the size of the record. We need all of
// it.
- if (bb.getChar() > bb.remaining()) {
- return false;
+ int size = bb.getChar();
+ if (bb.remaining() < size) {
+ return -1;
}
- return true;
+ return size;
+ }
+
+
+ private static boolean isClientHello(ByteBuffer bb) {
+ // Client hello is handshake type 1
+ if (bb.get() == 1) {
+ return true;
+ }
+ return false;
+ }
+
+
+ private static int clientHelloSize(ByteBuffer bb) {
+ // Next three bytes (unsigned) are the size of the client hello. We need
+ // all of it.
+ int size = ((bb.get() & 0xFF) << 16) + ((bb.get() & 0xFF) << 8) + (bb.get() & 0xFF);
+ if (bb.remaining() < size) {
+ return -1;
+ }
+ return size;
+ }
+
+ private static void swallowRandom(ByteBuffer bb) {
+ // 32 bytes total
+ for (int i = 0; i < 4; i++) {
+ bb.getLong();
+ }
+ }
+
+ private static void swallowCipherSuites(ByteBuffer bb) {
+ char c = bb.getChar();
+ for (int i = 0; i < c; i++) {
+ bb.get();
+ }
+ }
+
+
+ private static void swallowUnit8Vector(ByteBuffer bb) {
+ int b = bb.get() & 0xFF;
+ for (int i = 0; i < b; i++) {
+ bb.get();
+ }
+ }
+
+
+ private static String readSniExtension(ByteBuffer bb) {
+ // SNI extension is type 0
+ char extensionType = bb.getChar();
+ // Next byte is data size
+ char extensionDataSize = bb.getChar();
+ if (extensionType == 0) {
+ // First 2 bytes are size of server name list (only expecting one)
+ bb.getChar();
+ // Next byte is type (0 for hostname)
+ bb.get();
+ // Next 2 bytes are length of host name
+ char serverNameSize = bb.getChar();
+ byte[] serverNameBytes = new byte[serverNameSize];
+ bb.get(serverNameBytes);
+ return new String(serverNameBytes, StandardCharsets.UTF_8);
+ } else {
+ for (int i = 0; i < extensionDataSize; i++) {
+ bb.get();
+ }
+ }
+ return null;
}
public static enum SNIResult {
FOUND,
NOT_PRESENT,
UNDERFLOW,
- ERROR
+ NEED_READ
}
}
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java?rev=1672631&r1=1672630&r2=1672631&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/util/net/SecureNioChannel.java Fri Apr 10 12:36:31 2015
@@ -239,14 +239,16 @@ public class SecureNioChannel extends Ni
case FOUND:
hostName = extractor.getSNIValue();
break;
- case ERROR:
- // NO-OP Let the handshake continue and deal with whatever was wrong
- break;
case NOT_PRESENT:
// NO-OP
break;
case UNDERFLOW:
+ // Need to expand buffer
+ break;
+ case NEED_READ:
return SelectionKey.OP_READ;
+ default:
+ break;
}
System.out.println("SNI hostname was [" + hostName + "]");
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org