View Javadoc
1   /*
2    * Copyright 2020 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.quic;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.Unpooled;
20  import io.netty.channel.ChannelHandler;
21  import io.netty.channel.ChannelHandlerContext;
22  import io.netty.channel.ChannelOption;
23  import io.netty.channel.ChannelPromise;
24  import io.netty.channel.socket.DatagramPacket;
25  import io.netty.util.AttributeKey;
26  import io.netty.util.CharsetUtil;
27  import io.netty.util.internal.ObjectUtil;
28  import io.netty.util.internal.logging.InternalLogger;
29  import io.netty.util.internal.logging.InternalLoggerFactory;
30  import org.jetbrains.annotations.Nullable;
31  
32  import java.net.InetSocketAddress;
33  import java.net.SocketAddress;
34  import java.nio.ByteBuffer;
35  import java.util.Map;
36  import java.util.concurrent.Executor;
37  import java.util.function.Consumer;
38  import java.util.function.Function;
39  
40  /**
41   * {@link QuicheQuicCodec} for QUIC servers.
42   */
43  final class QuicheQuicServerCodec extends QuicheQuicCodec {
44      private static final InternalLogger LOGGER = InternalLoggerFactory.getInstance(QuicheQuicServerCodec.class);
45      private final Function<QuicChannel, ? extends QuicSslEngine> sslEngineProvider;
46      private final Executor sslTaskExecutor;
47      private final QuicConnectionIdGenerator connectionIdAddressGenerator;
48      private final QuicResetTokenGenerator resetTokenGenerator;
49      private final QuicTokenHandler tokenHandler;
50      private final ChannelHandler handler;
51      private final Map.Entry<ChannelOption<?>, Object>[] optionsArray;
52      private final Map.Entry<AttributeKey<?>, Object>[] attrsArray;
53      private final ChannelHandler streamHandler;
54      private final Map.Entry<ChannelOption<?>, Object>[] streamOptionsArray;
55      private final Map.Entry<AttributeKey<?>, Object>[] streamAttrsArray;
56      private ByteBuf mintTokenBuffer;
57      private ByteBuf connIdBuffer;
58  
59      QuicheQuicServerCodec(QuicheConfig config,
60                            int localConnIdLength,
61                            QuicTokenHandler tokenHandler,
62                            QuicConnectionIdGenerator connectionIdAddressGenerator,
63                            QuicResetTokenGenerator resetTokenGenerator,
64                            FlushStrategy flushStrategy,
65                            Function<QuicChannel, ? extends QuicSslEngine> sslEngineProvider,
66                            Executor sslTaskExecutor,
67                            ChannelHandler handler,
68                            Map.Entry<ChannelOption<?>, Object>[] optionsArray,
69                            Map.Entry<AttributeKey<?>, Object>[] attrsArray,
70                            ChannelHandler streamHandler,
71                            Map.Entry<ChannelOption<?>, Object>[] streamOptionsArray,
72                            Map.Entry<AttributeKey<?>, Object>[] streamAttrsArray) {
73          super(config, localConnIdLength, flushStrategy);
74          this.tokenHandler = tokenHandler;
75          this.connectionIdAddressGenerator = connectionIdAddressGenerator;
76          this.resetTokenGenerator = resetTokenGenerator;
77          this.sslEngineProvider = sslEngineProvider;
78          this.sslTaskExecutor = sslTaskExecutor;
79          this.handler = handler;
80          this.optionsArray = optionsArray;
81          this.attrsArray = attrsArray;
82          this.streamHandler = streamHandler;
83          this.streamOptionsArray = streamOptionsArray;
84          this.streamAttrsArray = streamAttrsArray;
85      }
86  
87      @Override
88      protected void handlerAdded(ChannelHandlerContext ctx, int localConnIdLength) {
89          connIdBuffer = Quiche.allocateNativeOrder(localConnIdLength);
90          mintTokenBuffer = Unpooled.directBuffer(tokenHandler.maxTokenLength());
91      }
92  
93      @Override
94      public void handlerRemoved(ChannelHandlerContext ctx) {
95          super.handlerRemoved(ctx);
96          if (connIdBuffer != null) {
97              connIdBuffer.release();
98          }
99          if (mintTokenBuffer != null) {
100             mintTokenBuffer.release();
101         }
102     }
103 
104     @Override
105     @Nullable
106     protected QuicheQuicChannel quicPacketRead(ChannelHandlerContext ctx, InetSocketAddress sender,
107                                                InetSocketAddress recipient, QuicPacketType type, long version,
108                                                ByteBuf scid, ByteBuf dcid, ByteBuf token,
109                                                ByteBuf senderSockaddrMemory, ByteBuf recipientSockaddrMemory,
110                                                Consumer<QuicheQuicChannel> freeTask, int localConnIdLength,
111                                                QuicheConfig config)
112             throws Exception {
113         ByteBuffer dcidByteBuffer = dcid.internalNioBuffer(dcid.readerIndex(), dcid.readableBytes());
114         QuicheQuicChannel channel = getChannel(dcidByteBuffer);
115         if (channel == null && type == QuicPacketType.INITIAL) {
116             // We only want to possibility create a new QuicChannel if this is the initial packet, otherwise
117             // drop the packet on the floor if we did not find a mapping before.
118             return handleServer(ctx, sender, recipient, type, version, scid, dcid, token,
119                     senderSockaddrMemory, recipientSockaddrMemory, freeTask, localConnIdLength, config);
120         }
121         return channel;
122     }
123 
124     private static void writePacket(ChannelHandlerContext ctx, int res, ByteBuf buffer, InetSocketAddress sender)
125             throws Exception {
126         if (res < 0) {
127             buffer.release();
128             if (res != Quiche.QUICHE_ERR_DONE) {
129                 throw Quiche.convertToException(res);
130             }
131         } else {
132             ctx.writeAndFlush(new DatagramPacket(buffer.writerIndex(buffer.writerIndex() + res), sender));
133         }
134     }
135 
136     @Nullable
137     private QuicheQuicChannel handleServer(ChannelHandlerContext ctx, InetSocketAddress sender,
138                                            InetSocketAddress recipient,
139                                            @SuppressWarnings("unused") QuicPacketType type, long version,
140                                            ByteBuf scid, ByteBuf dcid, ByteBuf token,
141                                            ByteBuf senderSockaddrMemory, ByteBuf recipientSockaddrMemory,
142                                            Consumer<QuicheQuicChannel> freeTask, int localConnIdLength,
143                                            QuicheConfig config) throws Exception {
144         // Version is an unsigned int.
145         if (!Quiche.quiche_version_is_supported((int) version)) {
146             // Version is not supported, try to negotiate it.
147             ByteBuf out = ctx.alloc().directBuffer(Quic.MAX_DATAGRAM_SIZE);
148 
149             int res = Quiche.quiche_negotiate_version(
150                     Quiche.readerMemoryAddress(scid), scid.readableBytes(),
151                     Quiche.readerMemoryAddress(dcid), dcid.readableBytes(),
152                     Quiche.writerMemoryAddress(out), out.writableBytes());
153             writePacket(ctx, res, out, sender);
154             return null;
155         }
156 
157         final QuicTokenHandler.TokenValidationResult validationResult;
158         boolean noToken = false;
159         if (!token.isReadable()) {
160             // Clear buffers so we can reuse these.
161             mintTokenBuffer.clear();
162             connIdBuffer.clear();
163 
164             // The remote peer did not send a token.
165             if (tokenHandler.writeToken(mintTokenBuffer, dcid, sender)) {
166                 ByteBuffer connId = connectionIdAddressGenerator.newId(
167                         scid.internalNioBuffer(scid.readerIndex(), scid.readableBytes()),
168                         dcid.internalNioBuffer(dcid.readerIndex(), dcid.readableBytes()),
169                         localConnIdLength);
170                 connIdBuffer.writeBytes(connId);
171 
172                 ByteBuf out = ctx.alloc().directBuffer(Quic.MAX_DATAGRAM_SIZE);
173                 int written = Quiche.quiche_retry(
174                         Quiche.readerMemoryAddress(scid), scid.readableBytes(),
175                         Quiche.readerMemoryAddress(dcid), dcid.readableBytes(),
176                         Quiche.readerMemoryAddress(connIdBuffer), connIdBuffer.readableBytes(),
177                         Quiche.readerMemoryAddress(mintTokenBuffer), mintTokenBuffer.readableBytes(),
178                         // unsigned int.
179                         (int) version,
180                         Quiche.writerMemoryAddress(out), out.writableBytes());
181 
182                 writePacket(ctx, written, out, sender);
183                 return null;
184             }
185             validationResult = null;
186             noToken = true;
187         } else {
188             // Slice the token and dcid before pass it to the QuicTokenHandler as the implementation might modify
189             // the readerIndex.
190             // See https://github.com/netty/netty-incubator-codec-quic/issues/742
191             validationResult = ObjectUtil.checkNotNull(
192                     tokenHandler.validateToken(token.slice(), sender, dcid.slice()), "validationResult");
193             if (!validationResult.isValid()) {
194                 if (LOGGER.isDebugEnabled()) {
195                     LOGGER.debug("invalid token: {}", token.toString(CharsetUtil.US_ASCII));
196                 }
197                 return null;
198             }
199         }
200 
201         final ByteBuffer key;
202         final long scidAddr;
203         final int scidLen;
204         final long ocidAddr;
205         final int ocidLen;
206 
207         if (noToken) {
208             connIdBuffer.clear();
209             key = connectionIdAddressGenerator.newId(
210                     scid.internalNioBuffer(scid.readerIndex(), scid.readableBytes()),
211                     dcid.internalNioBuffer(dcid.readerIndex(), dcid.readableBytes()),
212                     localConnIdLength);
213             connIdBuffer.writeBytes(key.duplicate());
214             scidAddr = Quiche.readerMemoryAddress(connIdBuffer);
215             scidLen = localConnIdLength;
216             ocidAddr = -1;
217             ocidLen = -1;
218 
219             QuicheQuicChannel existingChannel = getChannel(key);
220             if (existingChannel != null) {
221                 return existingChannel;
222             }
223         } else {
224             scidAddr = Quiche.readerMemoryAddress(dcid);
225             scidLen = localConnIdLength;
226             ByteBuf odcid = validationResult.originalDestinationConnectionId(token, dcid);
227             ocidLen = odcid.readableBytes();
228             ocidAddr = Quiche.memoryAddress(odcid, odcid.readerIndex(), ocidLen);
229             // Now create the key to store the channel in the map.
230             byte[] bytes = new byte[localConnIdLength];
231             dcid.getBytes(dcid.readerIndex(), bytes);
232             key = ByteBuffer.wrap(bytes);
233         }
234         QuicheQuicChannel channel = QuicheQuicChannel.forServer(
235                 ctx.channel(), key, recipient, sender, config.isDatagramSupported(),
236                 streamHandler, streamOptionsArray, streamAttrsArray, freeTask, sslTaskExecutor,
237                 connectionIdAddressGenerator, resetTokenGenerator);
238 
239         // We also need to add the original id as there might be multiple INITIAL packets.
240         byte[] originalId = new byte[dcid.readableBytes()];
241         dcid.getBytes(dcid.readerIndex(), originalId);
242         channel.sourceConnectionIds().add(ByteBuffer.wrap(originalId));
243 
244         Quic.setupChannel(channel, optionsArray, attrsArray, handler, LOGGER);
245         QuicSslEngine engine = sslEngineProvider.apply(channel);
246         if (!(engine instanceof QuicheQuicSslEngine)) {
247             channel.unsafe().closeForcibly();
248             throw new IllegalArgumentException("QuicSslEngine is not of type "
249                     + QuicheQuicSslEngine.class.getSimpleName());
250         }
251         if (engine.getUseClientMode()) {
252             channel.unsafe().closeForcibly();
253             throw new IllegalArgumentException("QuicSslEngine is not created in server mode");
254         }
255 
256         QuicheQuicSslEngine quicSslEngine = (QuicheQuicSslEngine) engine;
257         QuicheQuicConnection connection = quicSslEngine.createConnection(ssl -> {
258             ByteBuffer localAddrMemory =
259                     recipientSockaddrMemory.internalNioBuffer(0, recipientSockaddrMemory.capacity());
260             int localLen = SockaddrIn.setAddress(localAddrMemory, recipient);
261 
262             ByteBuffer peerAddrMemory = senderSockaddrMemory.internalNioBuffer(0, senderSockaddrMemory.capacity());
263             int peerLen = SockaddrIn.setAddress(peerAddrMemory, sender);
264             return Quiche.quiche_conn_new_with_tls(scidAddr, scidLen, ocidAddr, ocidLen,
265                     Quiche.memoryAddressWithPosition(localAddrMemory), localLen,
266                     Quiche.memoryAddressWithPosition(peerAddrMemory), peerLen,
267                     config.nativeAddress(), ssl, true);
268         });
269         if (connection  == null) {
270             channel.unsafe().closeForcibly();
271             LOGGER.debug("quiche_accept failed");
272             return null;
273         }
274 
275         channel.attachQuicheConnection(connection);
276 
277         addChannel(channel);
278 
279         ctx.channel().eventLoop().register(channel);
280         return channel;
281     }
282 
283     @Override
284     protected void connectQuicChannel(QuicheQuicChannel channel, SocketAddress remoteAddress,
285                                       SocketAddress localAddress, ByteBuf senderSockaddrMemory,
286                                       ByteBuf recipientSockaddrMemory, Consumer<QuicheQuicChannel> freeTask,
287                                       int localConnIdLength, QuicheConfig config, ChannelPromise promise) {
288         promise.setFailure(new UnsupportedOperationException());
289     }
290 }