1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.quic;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.Unpooled;
20
21 import java.net.InetSocketAddress;
22
23 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
24
25
26
27
28
29
30
31
32
33
34 public final class QuicHeaderParser implements AutoCloseable {
35
36 private static final int AES_128_GCM_TAG_LENGTH = 16;
37
38 private final int localConnectionIdLength;
39 private boolean closed;
40
41 public QuicHeaderParser(int localConnectionIdLength) {
42 this.localConnectionIdLength = checkPositiveOrZero(localConnectionIdLength, "localConnectionIdLength");
43 }
44
45 @Override
46 public void close() {
47 if (!closed) {
48 closed = true;
49 }
50 }
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public void parse(InetSocketAddress sender, InetSocketAddress recipient, ByteBuf packet,
69 QuicHeaderProcessor callback) throws Exception {
70 if (closed) {
71 throw new IllegalStateException(QuicHeaderParser.class.getSimpleName() + " is already closed");
72 }
73
74
75 int offset = 0;
76 int readable = packet.readableBytes();
77
78 checkReadable(offset, readable, Byte.BYTES);
79 byte first = packet.getByte(offset);
80 offset += Byte.BYTES;
81
82 final QuicPacketType type;
83 final long version;
84 final ByteBuf dcid;
85 final ByteBuf scid;
86 final ByteBuf token;
87
88 if (hasShortHeader(first)) {
89
90
91
92
93
94
95
96
97
98
99
100
101 version = 0;
102 type = QuicPacketType.SHORT;
103
104 scid = Unpooled.EMPTY_BUFFER;
105 token = Unpooled.EMPTY_BUFFER;
106 dcid = sliceCid(packet, offset, localConnectionIdLength);
107 } else {
108
109
110
111
112
113
114
115
116
117
118
119
120
121 checkReadable(offset, readable, Integer.BYTES);
122
123
124
125 version = packet.getUnsignedInt(offset);
126 offset += Integer.BYTES;
127 type = typeOfLongHeader(first, version);
128
129 int dcidLen = packet.getUnsignedByte(offset);
130 checkCidLength(dcidLen);
131 offset += Byte.BYTES;
132 dcid = sliceCid(packet, offset, dcidLen);
133 offset += dcidLen;
134
135 int scidLen = packet.getUnsignedByte(offset);
136 checkCidLength(scidLen);
137 offset += Byte.BYTES;
138 scid = sliceCid(packet, offset, scidLen);
139 offset += scidLen;
140 token = sliceToken(type, packet, offset, readable);
141 }
142 callback.process(sender, recipient, packet, type, version, scid, dcid, token);
143 }
144
145
146
147 private static void checkCidLength(int length) throws QuicException {
148 if (length > Quic.MAX_CONN_ID_LEN) {
149 throw new QuicException("connection id to large: " + length + " > " + Quic.MAX_CONN_ID_LEN,
150 QuicTransportError.PROTOCOL_VIOLATION);
151 }
152 }
153
154 private static ByteBuf sliceToken(QuicPacketType type, ByteBuf packet, int offset, int readable)
155 throws QuicException {
156 switch (type) {
157 case INITIAL:
158
159 checkReadable(offset, readable, Byte.BYTES);
160 int numBytes = numBytesForVariableLengthInteger(packet.getByte(offset));
161 int len = (int) getVariableLengthInteger(packet, offset, numBytes);
162 offset += numBytes;
163
164 checkReadable(offset, readable, len);
165 return packet.slice(offset, len);
166 case RETRY:
167
168
169 checkReadable(offset, readable, AES_128_GCM_TAG_LENGTH);
170 int tokenLen = readable - offset - AES_128_GCM_TAG_LENGTH;
171 return packet.slice(offset, tokenLen);
172 default:
173
174 return Unpooled.EMPTY_BUFFER;
175 }
176 }
177 private static QuicException newProtocolViolationException(String message) {
178 return new QuicException(message, QuicTransportError.PROTOCOL_VIOLATION);
179 }
180
181 static ByteBuf sliceCid(ByteBuf buffer, int offset, int len) throws QuicException {
182 checkReadable(offset, buffer.readableBytes(), len);
183 return buffer.slice(offset, len);
184 }
185
186 private static void checkReadable(int offset, int readable, int needed) throws QuicException {
187 int r = readable - offset;
188 if (r < needed) {
189 throw newProtocolViolationException("Not enough bytes to read, " + r + " < " + needed);
190 }
191 }
192
193
194
195
196
197
198 private static long getVariableLengthInteger(ByteBuf in, int offset, int len) throws QuicException {
199 checkReadable(offset, in.readableBytes(), len);
200 switch (len) {
201 case 1:
202 return in.getUnsignedByte(offset);
203 case 2:
204 return in.getUnsignedShort(offset) & 0x3fff;
205 case 4:
206 return in.getUnsignedInt(offset) & 0x3fffffff;
207 case 8:
208 return in.getLong(offset) & 0x3fffffffffffffffL;
209 default:
210 throw newProtocolViolationException("Unsupported length:" + len);
211 }
212 }
213
214
215
216
217
218
219 private static int numBytesForVariableLengthInteger(byte b) {
220 byte val = (byte) (b >> 6);
221 if ((val & 1) != 0) {
222 if ((val & 2) != 0) {
223 return 8;
224 }
225 return 2;
226 }
227 if ((val & 2) != 0) {
228 return 4;
229 }
230 return 1;
231 }
232
233 static boolean hasShortHeader(byte b) {
234 return (b & 0x80) == 0;
235 }
236
237 private static QuicPacketType typeOfLongHeader(byte first, long version) throws QuicException {
238 if (version == 0) {
239
240
241
242
243 return QuicPacketType.VERSION_NEGOTIATION;
244 }
245 int packetType = (first & 0x30) >> 4;
246 switch (packetType) {
247 case 0x00:
248 return QuicPacketType.INITIAL;
249 case 0x01:
250 return QuicPacketType.ZERO_RTT;
251 case 0x02:
252 return QuicPacketType.HANDSHAKE;
253 case 0x03:
254 return QuicPacketType.RETRY;
255 default:
256 throw newProtocolViolationException("Unknown packet type: " + packetType);
257 }
258 }
259
260
261
262
263 public interface QuicHeaderProcessor {
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 void process(InetSocketAddress sender, InetSocketAddress recipient, ByteBuf packet,
285 QuicPacketType type, long version, ByteBuf scid, ByteBuf dcid, ByteBuf token) throws Exception;
286 }
287 }