Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<DatagramPacket> {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {
Expand All @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<Object> out) throws Exception {

final ByteBuf content = msg.content();
if (parseLongPacketHeader(content)) {
return;
}

out.add(new QuicMessage(ReferenceCountUtil.retain(msg.content())));
}
}
Loading