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