供其他人重复使用,但这是我的第一次尝试,它的工作原理使我可以在握手开始之前获取 sniServerName...
public class ClientHelloParser {
private static final short HANDSHAKE_CONTENT_TYPE = 22;
private static final short CLIENTHELLO_MESSAGE_TYPE = 1;
private static final short SSLV2_CLIENTHELLO = 128;
private static final int SERVER_NAME_EXTENSION_TYPE = 0;
private static final short HOST_NAME_TYPE = 0;
private ByteBuffer cachedBuffer;
private BufferPool pool;
public ClientHelloParser(BufferPool pool) {
this.pool = pool;
}
/**
* Returns null if we still need more data
*
* @param b
* @return
*/
ParseResult fetchServerNamesIfEntirePacketAvailable(ByteBuffer b) {
if(cachedBuffer != null) {
//prefix cachedBuffer in front of b and assign to b as the packet that is coming in
ByteBuffer newBuf = pool.nextBuffer(cachedBuffer.remaining()+b.remaining());
newBuf.put(cachedBuffer);
newBuf.put(b);
newBuf.flip();
pool.releaseBuffer(b); //release b that is now in the newBuf
pool.releaseBuffer(cachedBuffer); //release cached buffer that is now in newBuf
b = newBuf;
}
if(b.remaining() < 5) {
cachedBuffer = b;
return null; //wait for more data
}
int recordSize = 0;
ByteBuffer duplicate = b.duplicate();
short contentType = getUnsignedByte(duplicate);
if(contentType == HANDSHAKE_CONTENT_TYPE) {
getUnsignedByte(duplicate);
getUnsignedByte(duplicate);
recordSize = getUnsignedShort(duplicate);
// Now wait until we have the entire record
if (b.remaining() < (5 + recordSize)) {
// Keep buffering
return null;
}
} else if (contentType == SSLV2_CLIENTHELLO) {
short len = getUnsignedByte(duplicate);
// Decode the length
recordSize = ((contentType & 0x7f) << 8 | len);
// Now wait until we have the entire record
if (b.remaining() < (2 + recordSize)) {
// Keep buffering
return null;
}
} else {
throw new IllegalStateException("contentType="+contentType+" not supported in ssl hello handshake packet");
}
short messageType = getUnsignedByte(duplicate);
if (messageType != CLIENTHELLO_MESSAGE_TYPE) {
throw new IllegalStateException("something came before ClientHello :( messageType="+messageType);
}
if (contentType == HANDSHAKE_CONTENT_TYPE) {
// If we're not an SSLv2 ClientHello, then skip the ClientHello
// message size.
duplicate.get(new byte[3]);
// Use the ClientHello ProtocolVersion
getUnsignedShort(duplicate);
// Skip ClientRandom
duplicate.get(new byte[32]);
// Skip SessionID
int sessionIDSize = getUnsignedByte(duplicate);
duplicate.get(new byte[sessionIDSize]);
//read in and discard cipherSuite...
int cipherSuiteSize = getUnsignedShort(duplicate);
duplicate.get(new byte[cipherSuiteSize]);
//read in compression methods size and discard..
short compressionMethodsLen = getUnsignedByte(duplicate);
duplicate.get(new byte[compressionMethodsLen]);
int extensionLen = getUnsignedShort(duplicate);
List<String> names = readInExtensionServerNames(duplicate, extensionLen);
return new ParseResult(b, names);
} else {
// SSLv2 ClientHello.
// Use the ClientHello ProtocolVersion
//SslVersion version = SslVersion.decode(getUnsignedByte(duplicate));
throw new UnsupportedOperationException("not supported yet");
}
}
private List<String> readInExtensionServerNames(ByteBuffer duplicate, int len) {
List<String> serverNames = new ArrayList<>();
int byteCount = 0;
while(byteCount < len) {
byteCount += 4; //reading in 4 bytes so add them in
if(duplicate.remaining() < 4)
throw new IllegalStateException("Corrupt packet with incorrect format");
int type = getUnsignedShort(duplicate);
int extLen = getUnsignedShort(duplicate);
if(duplicate.remaining() < extLen)
throw new IllegalStateException("Corrupt packet with incorrect format as len didn't match");
if(type == SERVER_NAME_EXTENSION_TYPE) {
String name = readServerNames(duplicate, extLen);
serverNames.add(name);
} else
duplicate.get(new byte[extLen]);
byteCount += extLen;
}
return serverNames;
}
private String readServerNames(ByteBuffer duplicate, int extLen) {
int byteCount = 0;
byteCount += 2; //for listLen 2 bytes
int listLen = getUnsignedShort(duplicate);
if(listLen + 2 != extLen)
throw new RuntimeException("we have something we need to fix here as listLen is only two less bytes then extensionLength");
byteCount += 1; //for serverNameType
short serverNameType = getUnsignedByte(duplicate);
if(serverNameType != HOST_NAME_TYPE)
throw new IllegalStateException("Server name type="+serverNameType+" not supported yet");
byteCount += 2; //for serverNameLen
int serverNameLen = getUnsignedShort(duplicate);
byteCount += serverNameLen;
if(byteCount != extLen)
throw new UnsupportedOperationException("bytes read in servernames extension does not match extLen(we need to loop here then)");
byte[] data = new byte[serverNameLen];
duplicate.get(data);
String serverName = new String(data);
return serverName;
}
public short getUnsignedByte(ByteBuffer bb) {
return ((short)(bb.get() & 0xff));
}
public int getUnsignedShort (ByteBuffer bb)
{
return (bb.getShort() & 0xffff);
}
}
我是一名优秀的程序员,十分优秀!