diff --git a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicFrame.java b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicFrame.java new file mode 100644 index 000000000000..e452f4153fe5 --- /dev/null +++ b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicFrame.java @@ -0,0 +1,27 @@ +package io.netty.handler.codec.quic; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +public class QuicFrame { + public static final byte CRYPTO_TYPE = 0x06; + + private final byte type; + private final byte[] offset; + private final byte[] length; + private final byte[] data; + + public QuicFrame(byte type, byte[] offset, byte[] length, byte[] data) { + this.type = type; + this.offset = offset; + this.length = length; + this.data = data; + } + + public ByteBuf toByteBuf() { + return Unpooled.buffer().writeByte(type) + .writeBytes(offset) + .writeBytes(length) + .writeBytes(data); + } +} diff --git a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicObjectDecoder.java b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicObjectDecoder.java new file mode 100644 index 000000000000..a5f5a084a68b --- /dev/null +++ b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicObjectDecoder.java @@ -0,0 +1,156 @@ +package io.netty.handler.codec.quic; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.socket.DatagramPacket; +import io.netty.handler.codec.MessageToMessageDecoder; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +public abstract class QuicObjectDecoder extends MessageToMessageDecoder { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(QuicObjectDecoder.class); + + private enum State { + INITIAL, + HEADER, + DCID_LEN, + DCID, + SCID_LEN, + SCID, + TOKEN_LEN, + TOKEN, + LENGTH, + PAYLOAD, + } + + static int typeSpecificBits(byte b) { + return b & 0x0f; + } + + static short longPacketType(byte b) { + return (short) ((b & 0x30) >> 4); + } + + static boolean fixedBit(byte b) { + return (b & 0x40) >> 6 == 1; + } + + static boolean headerForm(byte b) { + return (b & 0x80) >> 7 == 1; + } + + private int packetNumLength; + + private State state = State.INITIAL; + private int dcidLen; + private int scidLen; + private int version; + private int tokenLen; + private ByteBuf token; + private int payloadLength; + private ByteBuf payload; + + protected boolean parseLongPacketHeader(ByteBuf byteBuf) { + switch (state) { + case INITIAL: + if (!byteBuf.isReadable()) { + return true; + } + final byte headerByte = byteBuf.readByte(); + final boolean headerForm = headerForm(headerByte); + + // only process long header packets for now... + if (!headerForm) { + logger.info("headerForm: {}", headerForm); + return true; + } + + final boolean fixedBit = fixedBit(headerByte); + final short longPacketType = longPacketType(headerByte); + final int typeSpecificBits = typeSpecificBits(headerByte); + packetNumLength = headerByte & 0x3 + 1; + logger.info("headerForm: {}, fixedBit: {}, longPacketType: {}, typeSpecificBits: {}, packetNumLength: {}", + headerForm, fixedBit, longPacketType, typeSpecificBits, packetNumLength); + state = State.HEADER; + case HEADER: + if (!byteBuf.isReadable(4)) { + return true; + } + final ByteBuf versionByteBuf = byteBuf.readBytes(4); + version = versionByteBuf.copy().readInt(); + logger.info("version: {}", ByteBufUtil.prettyHexDump(versionByteBuf)); + state = State.DCID_LEN; + case DCID_LEN: + if (!byteBuf.isReadable()) { + return true; + } + dcidLen = byteBuf.readByte(); + logger.info("dcidLen: {}", dcidLen); + state = State.DCID; + case DCID: + if (!byteBuf.isReadable(dcidLen)) { + return true; + } + final ByteBuf dcid = byteBuf.readBytes(dcidLen); + logger.info("dcid: {}", ByteBufUtil.prettyHexDump(dcid)); + state = State.SCID_LEN; + case SCID_LEN: + if (!byteBuf.isReadable()) { + return true; + } + scidLen = byteBuf.readByte(); + logger.info("scidLen: {}", scidLen); + state = State.SCID; + case SCID: + if (!byteBuf.isReadable(scidLen)) { + return true; + } + final ByteBuf scid = byteBuf.readBytes(scidLen); + logger.info("scid: {}", ByteBufUtil.prettyHexDump(scid)); + case TOKEN_LEN: + if (!byteBuf.isReadable()) { + return true; + } + tokenLen = (int) QuicRequest.variableLengthIntegerDecoding(byteBuf); + logger.info("tokenLen: {}", tokenLen); + case TOKEN: + if (!byteBuf.isReadable(tokenLen)) { + return true; + } + token = byteBuf.readBytes(tokenLen); + logger.info("token: {}", token); + case LENGTH: + if (!byteBuf.isReadable()) { + return true; + } + payloadLength = (int) QuicRequest.variableLengthIntegerDecoding(byteBuf); + logger.info("payloadLength: {}", payloadLength); + case PAYLOAD: + if (!byteBuf.isReadable(payloadLength)) { + return true; + } + payload = byteBuf.readBytes(payloadLength); + logger.info("payload: {}", ByteBufUtil.prettyHexDump(payload)); + } + + ByteBuf copiedPayload = payload.copy(); + ByteBuf packetNumber = copiedPayload.readBytes(packetNumLength); + logger.info("packetNumber: {}", ByteBufUtil.prettyHexDump(packetNumber)); + + logger.info("first byte: {}", Integer.toBinaryString(copiedPayload.getByte(copiedPayload.readerIndex()) & 0xff)); + logger.info("first byte: {}", Integer.toHexString(copiedPayload.getByte(copiedPayload.readerIndex()) & 0xff)); + int firstFrameType = (int) QuicRequest.variableLengthIntegerDecoding(copiedPayload); + logger.info("firstFrameType: {}", firstFrameType); + + logger.info("remaining bytes: {}", ByteBufUtil.prettyHexDump(byteBuf)); + + // it will be identified as a Version Negotiation packet based on the Version field having a value of 0 + if (version == 0) { + while (byteBuf.readableBytes() > 0) { + ByteBuf versionBytBuf = byteBuf.readBytes(4); + logger.info("supported version hexdump: {}", ByteBufUtil.hexDump(versionBytBuf)); + } + } + return false; + } +} diff --git a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequest.java b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequest.java index 8d08c9b3de0f..ae149de634a8 100644 --- a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequest.java +++ b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequest.java @@ -22,9 +22,11 @@ public class QuicRequest extends QuicMessage { private final int tokenLength; private final byte[] packetNumber; + private final QuicFrame quicFrame; + public QuicRequest(InetSocketAddress inetSocketAddress, int headerForm, int fixedBit, int longPacketType, int typeSpecificBits, int version, byte[] dcid, byte[] scid, int tokenLength, - byte[] packetNumber) { + byte[] packetNumber, QuicFrame quicFrame) { this.inetSocketAddress = inetSocketAddress; this.headerForm = headerForm; this.fixedBit = fixedBit; @@ -35,6 +37,7 @@ public QuicRequest(InetSocketAddress inetSocketAddress, int headerForm, int fixe this.scid = scid.clone(); this.tokenLength = tokenLength; this.packetNumber = packetNumber.clone(); + this.quicFrame = quicFrame; } public InetSocketAddress getInetSocketAddress() { @@ -43,8 +46,8 @@ public InetSocketAddress getInetSocketAddress() { @Override public ByteBuf getByteBuf() { - byte header = (byte) (((headerForm & 0x01) << 7) + ((fixedBit & 0x01) << 6) + ((longPacketType & 0x03) << 5) + (typeSpecificBits & 0x0f)); - System.out.println(header); + final byte header = (byte) (((headerForm & 0x01) << 7) + ((fixedBit & 0x01) << 6) + ((longPacketType & 0x03) << 5) + (typeSpecificBits & 0x0f)); + final ByteBuf frameByteBuf = quicFrame.toByteBuf(); return Unpooled.buffer() .writeByte(header) .writeInt(version) @@ -53,8 +56,8 @@ public ByteBuf getByteBuf() { .writeByte(scid.length - 1) .writeBytes(scid) .writeByte(tokenLength) - .writeBytes(variableLengthIntegerEncoding(packetNumber.length)) - .writeBytes(packetNumber); + .writeBytes(variableLengthIntegerEncoding(packetNumber.length + 1 + frameByteBuf.array().length)) + .writeBytes(packetNumber).writeByte(0x01).writeBytes(quicFrame.toByteBuf()); } static byte[] variableLengthIntegerEncoding(long length) { @@ -84,4 +87,37 @@ static byte[] variableLengthIntegerEncoding(long length) { throw new IllegalArgumentException("invalid length: " + length); } } + + static long variableLengthIntegerDecoding(ByteBuf byteBuf) { + if (!byteBuf.isReadable()) { + throw new IllegalArgumentException("cannot read varint"); + } + final byte b1 = byteBuf.readByte(); + final int varIntLen = (b1 & 0xff) >> 6; + if (varIntLen == 0) { + return b1 & 0xff >> 2; + } + if (varIntLen == 1) { + final byte[] bytes = {b1, byteBuf.readByte()}; + return (bytes[0] & 0xff >> 2) << 8 | bytes[1] & 0xff; + } + if (varIntLen == 2) { + final byte[] bytes = {b1, byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte()}; + return (bytes[0] & 0xff >> 2) << 24 | (bytes[1] & 0xff) << 16 | + (bytes[2] & 0xff) << 8 | bytes[3] & 0xff; + } + if (varIntLen == 3) { + final byte[] bytes = {b1, byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte(), + byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte()}; + return Long.valueOf(bytes[0] & 0xff >> 2) << 56 | + Long.valueOf(bytes[1] & 0xff) << 48 | + Long.valueOf(bytes[2] & 0xff) << 40 | + Long.valueOf(bytes[3] & 0xff) << 32 | + Long.valueOf(bytes[4] & 0xff) << 24 | + Long.valueOf(bytes[5] & 0xff) << 16 | + Long.valueOf(bytes[6] & 0xff) << 8 | + Long.valueOf(bytes[7] & 0xff); + } + throw new IllegalArgumentException("invalid length: " + varIntLen); + } } diff --git a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequestDecoder.java b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequestDecoder.java new file mode 100644 index 000000000000..227e650ef44f --- /dev/null +++ b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicRequestDecoder.java @@ -0,0 +1,21 @@ +package io.netty.handler.codec.quic; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.DatagramPacket; +import io.netty.util.ReferenceCountUtil; + +import java.util.List; + +public class QuicRequestDecoder extends QuicObjectDecoder { + @Override + protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List out) throws Exception { + + final ByteBuf content = msg.content(); + if (parseLongPacketHeader(content)) { + return; + } + + out.add(new QuicMessage(ReferenceCountUtil.retain(msg.content()))); + } +} diff --git a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicResponseDecoder.java b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicResponseDecoder.java index e7d5d522d5e7..6381f9ebe2c0 100644 --- a/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicResponseDecoder.java +++ b/codec-quic/src/main/java/io/netty/handler/codec/quic/QuicResponseDecoder.java @@ -1,119 +1,22 @@ package io.netty.handler.codec.quic; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.List; -public class QuicResponseDecoder extends MessageToMessageDecoder { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(QuicResponseDecoder.class); - - private enum State { - INITIAL, - HEADER, - DCID_LEN, - DCID, - SCID_LEN, - SCID, - } - - private State state = State.INITIAL; - private int dcidLen; - private int scidLen; - private int version; +public class QuicResponseDecoder extends QuicObjectDecoder { @Override protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List out) throws Exception { final ByteBuf byteBuf = msg.content(); - switch (state) { - case INITIAL: - if (!byteBuf.isReadable()) { - return; - } - final byte headerByte = byteBuf.readByte(); - final boolean headerForm = headerForm(headerByte); - - // only process long header packets for now... - if (!headerForm) { - logger.info("headerForm: {}", headerForm); - return; - } - - final boolean fixedBit = fixedBit(headerByte); - final short longPacketType = longPacketType(headerByte); - final int typeSpecificBits = typeSpecificBits(headerByte); - logger.info("headerForm: {}, fixedBit: {}, longPacketType: {}, typeSpecificBits: {}", - headerForm, fixedBit, longPacketType, typeSpecificBits); - state = State.HEADER; - case HEADER: - if (!byteBuf.isReadable(4)) { - return; - } - final ByteBuf versionByteBuf = byteBuf.readBytes(4); - version = versionByteBuf.readInt(); - logger.info("version: {}", version); - state = State.DCID_LEN; - case DCID_LEN: - if (!byteBuf.isReadable()) { - return; - } - dcidLen = byteBuf.readByte(); - logger.info("dcidLen: {}", dcidLen); - state = State.DCID; - case DCID: - if (!byteBuf.isReadable()) { - return; - } - final ByteBuf dcid = byteBuf.readBytes(dcidLen); - logger.info("dcid: {}", dcid); - state = State.SCID_LEN; - case SCID_LEN: - if (!byteBuf.isReadable()) { - return; - } - scidLen = byteBuf.readByte(); - logger.info("scidLen: {}", scidLen); - state = State.SCID; - case SCID: - if (!byteBuf.isReadable()) { - return; - } - final ByteBuf scid = byteBuf.readBytes(scidLen); - logger.info("scid: {}", scid); - } - - // it will be identified as a Version Negotiation packet based on the Version field having a value of 0 - if (version == 0) { - while (byteBuf.readableBytes() > 0) { - ByteBuf versionBytBuf = byteBuf.readBytes(4); - logger.info("supported version hexdump: {}", ByteBufUtil.hexDump(versionBytBuf)); - } + if (parseLongPacketHeader(byteBuf)) { + return; } out.add(new QuicMessage(ReferenceCountUtil.retain(byteBuf))); } - - static int typeSpecificBits(byte b) { - return b & 0x0f; - } - - static short longPacketType(byte b) { - return (short) ((b & 0x30) >> 4); - } - - static boolean fixedBit(byte b) { - return (b & 0x40) >> 6 == 1; - } - - static boolean headerForm(byte b) { - return (b & 0x80) >> 7 == 1; - } } diff --git a/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicQuicTest.java b/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicQuicTest.java index 9c3436a8baa2..90cc4dc5ace6 100644 --- a/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicQuicTest.java +++ b/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicQuicTest.java @@ -30,7 +30,7 @@ public class BasicQuicTest { private static final String ECHO_MESSAGE = "hai:)"; private static final String CHROMIUM_ENDPOINT = "https://quic.rocks:4433/"; - + private static final String SAMPLE_INITIAL_HEXDUMP_PACKET = "cfff00001810b57f88e894e16fda41d0f29f49677f751477bc1a6f2950e4a601dd16df156bb93535e08711004482f6fff9222619c5ff07a85338139476ccda979adbc2f04e90fbbf8f8266af3005306fbba8b0f7dc6b89e0a4485ccfeaa8fe2d6c59bc0e1b376f6f02c5bc8ac90404bd7d2b3accd2c11e519092042957e9863a9ce927b7176f7536c46489ca7710b6e485b53edc43f44f5108f6a52d783a64d24857ba2ce24b5eeafe39cf02e8547fdb814e67d5c3c60045ce6c6a4f0113c895502c63f5c52c4c6dea34c91cc0691f6764507fc0f63af13a59728b063ada930234ba9b05f5f7d4add36b15028a2d892a55c798e96bbdc3bb91490cde96c9f37d336baa287584cc93101d5284ac340bbd29c3548c098532446e0bb95508fb878c33bc21a34273d15552a0fd450da2748700550ea33c187d0fd7bb0a94b57eb0803be430483118e8fe98fbeb8608436d8f39abe30c28fba360fc4675e401d64c1bd87bdfb647422a05432d7aaeeb600c85bbfbb99bda07deb2a5faf17f8c1b5de461fb6c41faa20338e54531b3080893af30c8aab269fef41051772df2b0a05ba393505b29c8b9edfd3085f359a64c2c8749aeb82e1ac7d0f3234af47b7ec9a51dbb81debbfc72490aad40c5377298d8cf0b21bf8f8750dfe3b45833f6a425d9cd8daaeb1aac1c44e69f198b5a9015364177bcba7f5022b6fdaf123de110360b0dd619a37dc96cbe2809df1e481a7e3b500911cb49c1bfe8dca4c6230ab816f187875a4c70198534f4e3e77ec76341f00135d3d7b701f3a1f2e6743b45d3af9a00fe4938fba2ab30cb3d3c67d0a237ab0be45e1229b72280bdec7e61c01da2329967a21ced88adc79e73818eb8aa583591c85f34dfe1231b70c5769cd8945bd642b607453c853b2061fd259b57e09743e54d6cd7b6344a1e9a817b94e2272f797a0a171ac14de318986bdf723eab648f3deeb4e18211f37e79a014e7b39ff07bf84b353936de53868f70a7a3d1be70186e741eeff84461afe9905a85ea18f0ec3586f332f3ef2db99921be70f2b89450b7e45b2ae381d4ef4e32abbfd5c11736da4c871bc7eacab8a5b7238925fd3cb8acd824de004b55bdc78bc6f518666772cd945444101d27f5141926246d7e8d95009ed2763d0bf88411f5eace577850d04d15e172116fc1fcd7d246d0a837627b6e7a610365e343d7544dd46c799daf74c05ed9c1a6b71c3598b9b4f7461f45cb88c6a6005269b9f8b1b3aefad2e7c3a12831d428337699fc414bde0320dc334ba06ac88c3522f8636b50c4bc15a1d088757c62882d5495b06d4d83fee1ecff34ab20f5e7003aae70a962d7ded730fc5ee75cb2fb8601429bec3211a70c194ba903bb38e1f182612dedfad96702c6cf55046fe04f89a3534d4006f288ffbd38d89dbbbd9d37ab5c188ee45264410f9fea51d393710b1ae834a80d91edda4aa63f60bfa582bb4451408f30b1e4dbef075b6f6554b7ac31ed7520158ee2fe7bff6a7038762826c6c40553f5b567f06fc75b3f387e9ddf3f1486348158868de7166679442cdee500ef61fb76dcf80d5a7c819831276e86d8736cdb21ee2aa22e0ca55c83bafc56e74be1ad935150653f6c34dd39bb67ab2357a873f0428df56d1c6f9a583c5feac8cfa71da32b8527d0de7baf725959b862b915bd091a7ea9ab6e4e72"; @Test public void testSimpleEcho() throws Throwable { @@ -52,8 +52,8 @@ public void testSimpleEcho() throws Throwable { assertEquals(ECHO_MESSAGE, result.get()); - serverChannel.closeFuture().await(); - clientChannel.closeFuture().await(); + serverChannel.close().await(); + clientChannel.close().await(); } @Test @@ -66,8 +66,13 @@ public void trySendInitialPacket() throws Exception { result.set(Unpooled.copiedBuffer(res)); latch.countDown(); }); - client.writeAndFlush(new QuicRequest(remote, 1, 1, 0, 0, - 1, new byte[] {0}, new byte[] {0}, 0, new byte[] {0})).sync(); + +// final QuicFrame quicFrame = new QuicFrame((byte) 0x06, new byte[]{0}, new byte[] {0}, new byte[]{0}); +// client.writeAndFlush(new QuicRequest(remote, 1, 1, 0, 0, +// 0x00000001, new byte[] {0}, new byte[] {0}, 0, new byte[] {0}, +// quicFrame)).sync(); + client.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump( + SAMPLE_INITIAL_HEXDUMP_PACKET)), remote)).sync(); latch.await(); assertNotNull(result.get()); diff --git a/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicSslTest.java b/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicSslTest.java new file mode 100644 index 000000000000..126db5ab59de --- /dev/null +++ b/codec-quic/src/test/java/io/netty/handler/codec/quic/BasicSslTest.java @@ -0,0 +1,181 @@ +package io.netty.handler.codec.quic; + +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import org.junit.Test; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; +import javax.net.ssl.SSLException; +import java.nio.ByteBuffer; +import java.security.cert.CertificateException; + +public class BasicSslTest { + + private volatile SSLEngine clientSslEngine; + private volatile SSLEngine serverSslEngine; + + private final SelfSignedCertificate cert = new SelfSignedCertificate(); + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); + + public BasicSslTest() throws CertificateException { + } + + private SSLEngine clientSslEngine() throws SSLException { + if (clientSslEngine == null) { + synchronized (this) { + if (clientSslEngine == null) { + final SslContext sslContext = SslContextBuilder.forClient().trustManager(cert.cert()).protocols( + "TLSv1.2").build(); + clientSslEngine = sslContext.newEngine(UnpooledByteBufAllocator.DEFAULT); + } + } + } + return clientSslEngine; + } + + private SSLEngine serverSslEngine() throws SSLException, CertificateException { + if (serverSslEngine == null) { + synchronized (this) { + if (serverSslEngine == null) { + + final SslContext sslContext = SslContextBuilder.forServer( + cert.certificate(), cert.privateKey()).protocols("TLSv1", "TLSv1.1", "TLSv1.2").build(); + serverSslEngine = sslContext.newEngine(UnpooledByteBufAllocator.DEFAULT); + } + } + } + return serverSslEngine; + } + + @Test + public void sandboxSslTest() throws Exception { + + ByteBuffer clientPacketBuf = ByteBuffer.allocate(clientSslEngine().getSession().getPacketBufferSize()); + ByteBuffer serverPacketBuf = ByteBuffer.allocate(serverSslEngine().getSession().getPacketBufferSize()); + + ByteBuffer serverAppBuf = ByteBuffer.allocate( + serverSslEngine().getSession().getApplicationBufferSize()); + ByteBuffer clientAppBuf = ByteBuffer.allocate( + clientSslEngine().getSession().getApplicationBufferSize()); + + // Start client side handshaking + assert clientSslEngine().getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING; + + clientSslEngine().beginHandshake(); + + assert clientSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + + SSLEngineResult result = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf); + + assert result.getStatus() == Status.OK; + assert result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP; + + assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING; + + serverSslEngine().beginHandshake(); + + assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP; + + clientPacketBuf.flip(); + SSLEngineResult serverResult1 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf); + clientPacketBuf.compact(); + + assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_TASK; + assert serverResult1.getStatus() == Status.OK; + + Runnable task; + while ((task = serverSslEngine().getDelegatedTask()) != null) { + task.run(); + } + + assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + + SSLEngineResult serverResult2 = serverSslEngine().wrap(EMPTY_BUFFER, serverPacketBuf); + + assert serverResult2.getStatus() == Status.OK; + + serverPacketBuf.flip(); + SSLEngineResult clientResult2 = clientSslEngine().unwrap(serverPacketBuf, clientAppBuf); + serverPacketBuf.compact(); + + assert clientResult2.getHandshakeStatus() == HandshakeStatus.NEED_TASK; + assert clientResult2.getStatus() == Status.OK; + + while ((task = clientSslEngine().getDelegatedTask()) != null) { + task.run(); + } + + assert clientSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + + SSLEngineResult clientResult3 = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf); + + assert clientResult3.getStatus() == Status.OK; + assert clientResult3.getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + + clientPacketBuf.flip(); + SSLEngineResult serverResult3 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf); + clientPacketBuf.compact(); + + assert serverResult3.getStatus() == Status.OK; + assert serverResult3.getHandshakeStatus() == HandshakeStatus.NEED_TASK; + + while ((task = serverSslEngine().getDelegatedTask()) != null) { + task.run(); + } + + assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP; + + SSLEngineResult clientResult4 = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf); + + assert clientResult4.getStatus() == Status.OK; + assert clientResult4.getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + + clientPacketBuf.flip(); + SSLEngineResult serverResult4 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf); + clientPacketBuf.compact(); + + assert serverResult4.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP; + assert serverResult4.getStatus() == Status.OK; + + SSLEngineResult clientResult5 = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf); + + assert clientResult5.getStatus() == Status.OK; + assert clientResult5.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP; + + clientPacketBuf.flip(); + SSLEngineResult serverResult5 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf); + clientPacketBuf.compact(); + + assert serverResult5.getStatus() == Status.OK; + assert serverResult5.getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + + SSLEngineResult serverResult6 = serverSslEngine().wrap(EMPTY_BUFFER, serverPacketBuf); + + assert serverResult6.getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + assert serverResult6.getStatus() == Status.OK; + + serverPacketBuf.flip(); + SSLEngineResult clientResult6 = clientSslEngine().unwrap(serverPacketBuf, clientAppBuf); + serverPacketBuf.compact(); + + assert clientResult6.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP; + assert clientResult6.getStatus() == Status.OK; + + SSLEngineResult serverResult7 = serverSslEngine().wrap(EMPTY_BUFFER, serverPacketBuf); + + assert serverResult7.getHandshakeStatus() == HandshakeStatus.FINISHED; + assert serverResult7.getStatus() == Status.OK; + + serverPacketBuf.flip(); + SSLEngineResult clientResult7 = clientSslEngine().unwrap(serverPacketBuf, clientAppBuf); + serverPacketBuf.compact(); + + assert clientResult7.getStatus() == Status.OK; + assert clientResult7.getHandshakeStatus() == HandshakeStatus.FINISHED; + } +} diff --git a/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestDecoderTest.java b/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestDecoderTest.java new file mode 100644 index 000000000000..433ab20b3a8b --- /dev/null +++ b/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestDecoderTest.java @@ -0,0 +1,28 @@ +package io.netty.handler.codec.quic; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.channel.socket.DatagramPacket; +import org.junit.Test; + +import java.net.InetSocketAddress; + +public class QuicRequestDecoderTest { + private static final String SAMPLE_INITIAL_HEXDUMP_PACKET = "cfff00001810b57f88e894e16fda41d0f29f49677f751477bc1a6f2950e4a601dd16df156bb93535e08711004482f6fff9222619c5ff07a85338139476ccda979adbc2f04e90fbbf8f8266af3005306fbba8b0f7dc6b89e0a4485ccfeaa8fe2d6c59bc0e1b376f6f02c5bc8ac90404bd7d2b3accd2c11e519092042957e9863a9ce927b7176f7536c46489ca7710b6e485b53edc43f44f5108f6a52d783a64d24857ba2ce24b5eeafe39cf02e8547fdb814e67d5c3c60045ce6c6a4f0113c895502c63f5c52c4c6dea34c91cc0691f6764507fc0f63af13a59728b063ada930234ba9b05f5f7d4add36b15028a2d892a55c798e96bbdc3bb91490cde96c9f37d336baa287584cc93101d5284ac340bbd29c3548c098532446e0bb95508fb878c33bc21a34273d15552a0fd450da2748700550ea33c187d0fd7bb0a94b57eb0803be430483118e8fe98fbeb8608436d8f39abe30c28fba360fc4675e401d64c1bd87bdfb647422a05432d7aaeeb600c85bbfbb99bda07deb2a5faf17f8c1b5de461fb6c41faa20338e54531b3080893af30c8aab269fef41051772df2b0a05ba393505b29c8b9edfd3085f359a64c2c8749aeb82e1ac7d0f3234af47b7ec9a51dbb81debbfc72490aad40c5377298d8cf0b21bf8f8750dfe3b45833f6a425d9cd8daaeb1aac1c44e69f198b5a9015364177bcba7f5022b6fdaf123de110360b0dd619a37dc96cbe2809df1e481a7e3b500911cb49c1bfe8dca4c6230ab816f187875a4c70198534f4e3e77ec76341f00135d3d7b701f3a1f2e6743b45d3af9a00fe4938fba2ab30cb3d3c67d0a237ab0be45e1229b72280bdec7e61c01da2329967a21ced88adc79e73818eb8aa583591c85f34dfe1231b70c5769cd8945bd642b607453c853b2061fd259b57e09743e54d6cd7b6344a1e9a817b94e2272f797a0a171ac14de318986bdf723eab648f3deeb4e18211f37e79a014e7b39ff07bf84b353936de53868f70a7a3d1be70186e741eeff84461afe9905a85ea18f0ec3586f332f3ef2db99921be70f2b89450b7e45b2ae381d4ef4e32abbfd5c11736da4c871bc7eacab8a5b7238925fd3cb8acd824de004b55bdc78bc6f518666772cd945444101d27f5141926246d7e8d95009ed2763d0bf88411f5eace577850d04d15e172116fc1fcd7d246d0a837627b6e7a610365e343d7544dd46c799daf74c05ed9c1a6b71c3598b9b4f7461f45cb88c6a6005269b9f8b1b3aefad2e7c3a12831d428337699fc414bde0320dc334ba06ac88c3522f8636b50c4bc15a1d088757c62882d5495b06d4d83fee1ecff34ab20f5e7003aae70a962d7ded730fc5ee75cb2fb8601429bec3211a70c194ba903bb38e1f182612dedfad96702c6cf55046fe04f89a3534d4006f288ffbd38d89dbbbd9d37ab5c188ee45264410f9fea51d393710b1ae834a80d91edda4aa63f60bfa582bb4451408f30b1e4dbef075b6f6554b7ac31ed7520158ee2fe7bff6a7038762826c6c40553f5b567f06fc75b3f387e9ddf3f1486348158868de7166679442cdee500ef61fb76dcf80d5a7c819831276e86d8736cdb21ee2aa22e0ca55c83bafc56e74be1ad935150653f6c34dd39bb67ab2357a873f0428df56d1c6f9a583c5feac8cfa71da32b8527d0de7baf725959b862b915bd091a7ea9ab6e4e72"; + + @Test + public void testEncoding() { + final byte[] bytes = ByteBufUtil.decodeHexDump(SAMPLE_INITIAL_HEXDUMP_PACKET); + final ByteBuf byteBuf = Unpooled.copiedBuffer(bytes); + final InetSocketAddress remote = new InetSocketAddress(0); + final DatagramPacket datagramPacket = new DatagramPacket(byteBuf, remote); + + final EmbeddedChannel channel = new EmbeddedChannel(new QuicRequestDecoder()); + channel.writeInbound(datagramPacket); + + final QuicMessage quicMessage = channel.readInbound(); + System.out.println(quicMessage); + } +} diff --git a/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestTest.java b/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestTest.java index 18c6c6ff15c8..3cf005f41723 100644 --- a/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestTest.java +++ b/codec-quic/src/test/java/io/netty/handler/codec/quic/QuicRequestTest.java @@ -1,6 +1,8 @@ package io.netty.handler.codec.quic; +import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import org.junit.Test; import static org.junit.Assert.*; @@ -14,6 +16,13 @@ public void test1byteEncoding() { assertEquals("25", ByteBufUtil.hexDump(retval)); } + @Test + public void test1byteDecoding() { + final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("25")); + final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf); + assertEquals(37, result); + } + @Test public void test2byteEncoding() { final long length = 15293; @@ -21,6 +30,13 @@ public void test2byteEncoding() { assertEquals("7bbd", ByteBufUtil.hexDump(retval)); } + @Test + public void test2byteDecoding() { + final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("7bbd")); + final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf); + assertEquals(15293, result); + } + @Test public void test4byteEncoding() { final long length = 494878333; @@ -28,10 +44,24 @@ public void test4byteEncoding() { assertEquals("9d7f3e7d", ByteBufUtil.hexDump(retval)); } + @Test + public void test4byteDecoding() { + final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("9d7f3e7d")); + final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf); + assertEquals(494878333, result); + } + @Test public void test8byteEncoding() { final long length = 151288809941952652L; final byte[] retval = QuicRequest.variableLengthIntegerEncoding(length); assertEquals("c2197c5eff14e88c", ByteBufUtil.hexDump(retval)); } + + @Test + public void test8byteDecoding() { + final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("c2197c5eff14e88c")); + final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf); + assertEquals(151288809941952652L, result); + } }