View Javadoc
1   /*
2    * Copyright 2016 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.channel.kqueue;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.channel.AddressedEnvelope;
21  import io.netty.channel.ChannelFuture;
22  import io.netty.channel.ChannelPipeline;
23  import io.netty.channel.ChannelPromise;
24  import io.netty.channel.DefaultAddressedEnvelope;
25  import io.netty.channel.socket.DatagramChannel;
26  import io.netty.channel.socket.DatagramChannelConfig;
27  import io.netty.channel.socket.DatagramPacket;
28  import io.netty.channel.socket.InternetProtocolFamily;
29  import io.netty.channel.socket.SocketProtocolFamily;
30  import io.netty.channel.unix.DatagramSocketAddress;
31  import io.netty.channel.unix.Errors;
32  import io.netty.channel.unix.IovArray;
33  import io.netty.channel.unix.UnixChannelUtil;
34  import io.netty.util.UncheckedBooleanSupplier;
35  import io.netty.util.internal.ObjectUtil;
36  import io.netty.util.internal.StringUtil;
37  
38  import java.io.IOException;
39  import java.net.Inet4Address;
40  import java.net.InetAddress;
41  import java.net.InetSocketAddress;
42  import java.net.NetworkInterface;
43  import java.net.PortUnreachableException;
44  import java.net.SocketAddress;
45  import java.net.SocketException;
46  import java.nio.ByteBuffer;
47  import java.nio.channels.UnresolvedAddressException;
48  
49  import static io.netty.channel.kqueue.BsdSocket.newSocketDgram;
50  
51  public final class KQueueDatagramChannel extends AbstractKQueueDatagramChannel implements DatagramChannel {
52      private static final String EXPECTED_TYPES =
53              " (expected: " + StringUtil.simpleClassName(DatagramPacket.class) + ", " +
54                      StringUtil.simpleClassName(AddressedEnvelope.class) + '<' +
55                      StringUtil.simpleClassName(ByteBuf.class) + ", " +
56                      StringUtil.simpleClassName(InetSocketAddress.class) + ">, " +
57                      StringUtil.simpleClassName(ByteBuf.class) + ')';
58  
59      private volatile boolean connected;
60      private final KQueueDatagramChannelConfig config;
61  
62      public KQueueDatagramChannel() {
63          super(null, newSocketDgram(), false);
64          config = new KQueueDatagramChannelConfig(this);
65      }
66  
67      /**
68       * @deprecated use {@link KQueueDatagramChannel#KQueueDatagramChannel(SocketProtocolFamily)}
69       */
70      @Deprecated
71      public KQueueDatagramChannel(InternetProtocolFamily protocol) {
72          super(null, newSocketDgram(protocol), false);
73          config = new KQueueDatagramChannelConfig(this);
74      }
75  
76      public KQueueDatagramChannel(SocketProtocolFamily protocol) {
77          super(null, newSocketDgram(protocol), false);
78          config = new KQueueDatagramChannelConfig(this);
79      }
80  
81      public KQueueDatagramChannel(int fd) {
82          this(new BsdSocket(fd), true);
83      }
84  
85      KQueueDatagramChannel(BsdSocket socket, boolean active) {
86          super(null, socket, active);
87          config = new KQueueDatagramChannelConfig(this);
88      }
89  
90      @Override
91      public InetSocketAddress remoteAddress() {
92          return (InetSocketAddress) super.remoteAddress();
93      }
94  
95      @Override
96      public InetSocketAddress localAddress() {
97          return (InetSocketAddress) super.localAddress();
98      }
99  
100     @Override
101     @SuppressWarnings("deprecation")
102     public boolean isActive() {
103         return socket.isOpen() && (config.getActiveOnOpen() && isRegistered() || active);
104     }
105 
106     @Override
107     public boolean isConnected() {
108         return connected;
109     }
110 
111     @Override
112     public ChannelFuture joinGroup(InetAddress multicastAddress) {
113         return joinGroup(multicastAddress, newPromise());
114     }
115 
116     @Override
117     public ChannelFuture joinGroup(InetAddress multicastAddress, ChannelPromise promise) {
118         try {
119             return joinGroup(multicastAddress, defaultMulticastInterface(), null, promise);
120         } catch (SocketException e) {
121             promise.setFailure(e);
122         }
123         return promise;
124     }
125 
126     /**
127      * Returns the interface to use when none was given: the one set via
128      * {@link io.netty.channel.ChannelOption#IP_MULTICAST_IF} or, if unset, the interface of the local address.
129      */
130     private NetworkInterface defaultMulticastInterface() throws SocketException {
131         NetworkInterface iface = config().getNetworkInterface();
132         if (iface == null) {
133             iface = NetworkInterface.getByInetAddress(localAddress().getAddress());
134         }
135         return iface;
136     }
137 
138     @Override
139     public ChannelFuture joinGroup(
140             InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
141         return joinGroup(multicastAddress, networkInterface, newPromise());
142     }
143 
144     @Override
145     public ChannelFuture joinGroup(
146             InetSocketAddress multicastAddress, NetworkInterface networkInterface,
147             ChannelPromise promise) {
148         return joinGroup(multicastAddress.getAddress(), networkInterface, null, promise);
149     }
150 
151     @Override
152     public ChannelFuture joinGroup(
153             InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
154         return joinGroup(multicastAddress, networkInterface, source, newPromise());
155     }
156 
157     @Override
158     public ChannelFuture joinGroup(
159             final InetAddress multicastAddress, final NetworkInterface networkInterface,
160             final InetAddress source, final ChannelPromise promise) {
161 
162         ObjectUtil.checkNotNull(multicastAddress, "multicastAddress");
163         ObjectUtil.checkNotNull(networkInterface, "networkInterface");
164 
165         promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
166         return promise;
167     }
168 
169     @Override
170     public ChannelFuture leaveGroup(InetAddress multicastAddress) {
171         return leaveGroup(multicastAddress, newPromise());
172     }
173 
174     @Override
175     public ChannelFuture leaveGroup(InetAddress multicastAddress, ChannelPromise promise) {
176         try {
177             return leaveGroup(multicastAddress, defaultMulticastInterface(), null, promise);
178         } catch (SocketException e) {
179             promise.setFailure(e);
180         }
181         return promise;
182     }
183 
184     @Override
185     public ChannelFuture leaveGroup(
186             InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
187         return leaveGroup(multicastAddress, networkInterface, newPromise());
188     }
189 
190     @Override
191     public ChannelFuture leaveGroup(
192             InetSocketAddress multicastAddress,
193             NetworkInterface networkInterface, ChannelPromise promise) {
194         return leaveGroup(multicastAddress.getAddress(), networkInterface, null, promise);
195     }
196 
197     @Override
198     public ChannelFuture leaveGroup(
199             InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
200         return leaveGroup(multicastAddress, networkInterface, source, newPromise());
201     }
202 
203     @Override
204     public ChannelFuture leaveGroup(
205             final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source,
206             final ChannelPromise promise) {
207         ObjectUtil.checkNotNull(multicastAddress, "multicastAddress");
208         ObjectUtil.checkNotNull(networkInterface, "networkInterface");
209 
210         promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
211 
212         return promise;
213     }
214 
215     @Override
216     public ChannelFuture block(
217             InetAddress multicastAddress, NetworkInterface networkInterface,
218             InetAddress sourceToBlock) {
219         return block(multicastAddress, networkInterface, sourceToBlock, newPromise());
220     }
221 
222     @Override
223     public ChannelFuture block(
224             final InetAddress multicastAddress, final NetworkInterface networkInterface,
225             final InetAddress sourceToBlock, final ChannelPromise promise) {
226         ObjectUtil.checkNotNull(multicastAddress, "multicastAddress");
227         ObjectUtil.checkNotNull(sourceToBlock, "sourceToBlock");
228         ObjectUtil.checkNotNull(networkInterface, "networkInterface");
229         promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
230         return promise;
231     }
232 
233     @Override
234     public ChannelFuture block(InetAddress multicastAddress, InetAddress sourceToBlock) {
235         return block(multicastAddress, sourceToBlock, newPromise());
236     }
237 
238     @Override
239     public ChannelFuture block(
240             InetAddress multicastAddress, InetAddress sourceToBlock, ChannelPromise promise) {
241         try {
242             return block(multicastAddress, defaultMulticastInterface(), sourceToBlock, promise);
243         } catch (Throwable e) {
244             promise.setFailure(e);
245         }
246         return promise;
247     }
248 
249     @Override
250     protected AbstractKQueueUnsafe newUnsafe() {
251         return new KQueueDatagramChannelUnsafe();
252     }
253 
254     @Override
255     protected void doBind(SocketAddress localAddress) throws Exception {
256         if (localAddress instanceof InetSocketAddress) {
257             InetSocketAddress socketAddress = (InetSocketAddress) localAddress;
258             if (socketAddress.getAddress().isAnyLocalAddress() &&
259                     socketAddress.getAddress() instanceof Inet4Address) {
260                 if (socket.family() == SocketProtocolFamily.INET6) {
261                     localAddress = new InetSocketAddress(Native.INET6_ANY, socketAddress.getPort());
262                 }
263             }
264         }
265         super.doBind(localAddress);
266         active = true;
267     }
268 
269     @Override
270     protected boolean doWriteMessage(Object msg) throws Exception {
271         final ByteBuf data;
272         InetSocketAddress remoteAddress;
273         if (msg instanceof AddressedEnvelope) {
274             @SuppressWarnings("unchecked")
275             AddressedEnvelope<ByteBuf, InetSocketAddress> envelope =
276                     (AddressedEnvelope<ByteBuf, InetSocketAddress>) msg;
277             data = envelope.content();
278             remoteAddress = envelope.recipient();
279         } else {
280             data = (ByteBuf) msg;
281             remoteAddress = null;
282         }
283 
284         final int dataLen = data.readableBytes();
285         final long writtenBytes;
286         if (data.hasMemoryAddress()) {
287             long memoryAddress = data.memoryAddress();
288             if (remoteAddress == null) {
289                 try {
290                     writtenBytes = socket.writeAddress(memoryAddress, data.readerIndex(), data.writerIndex());
291                 } catch (Errors.NativeIoException e) {
292                     throw translateForConnected(e);
293                 }
294             } else {
295                 writtenBytes = socket.sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
296                         remoteAddress.getAddress(), remoteAddress.getPort());
297             }
298         } else if (data.nioBufferCount() > 1) {
299             IovArray array = ((NativeArrays) registration().attachment()).cleanIovArray();
300             array.add(data, data.readerIndex(), data.readableBytes());
301             int cnt = array.count();
302             assert cnt != 0;
303 
304             if (remoteAddress == null) {
305                 writtenBytes = socket.writevAddresses(array.memoryAddress(0), cnt);
306             } else {
307                 writtenBytes = socket.sendToAddresses(array.memoryAddress(0), cnt,
308                         remoteAddress.getAddress(), remoteAddress.getPort());
309             }
310         } else {
311             ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes());
312             if (remoteAddress == null) {
313                 writtenBytes = socket.write(nioData, nioData.position(), nioData.limit());
314             } else {
315                 writtenBytes = socket.sendTo(nioData, nioData.position(), nioData.limit(),
316                         remoteAddress.getAddress(), remoteAddress.getPort());
317             }
318         }
319 
320         // A zero-length datagram is valid; sendto() returns 0 for it.
321         return writtenBytes > 0 || dataLen == 0;
322     }
323 
324     private static IOException translateForConnected(Errors.NativeIoException e) {
325         // We need to correctly translate connect errors to match NIO behaviour.
326         if (e.expectedErr() == Errors.ERROR_ECONNREFUSED_NEGATIVE) {
327             PortUnreachableException error = new PortUnreachableException(e.getMessage());
328             error.initCause(e);
329             return error;
330         }
331         return e;
332     }
333 
334     private static void checkUnresolved(AddressedEnvelope<?, ?> envelope) {
335         if (envelope.recipient() instanceof InetSocketAddress
336                 && (((InetSocketAddress) envelope.recipient()).isUnresolved())) {
337             throw new UnresolvedAddressException();
338         }
339     }
340 
341     @Override
342     protected Object filterOutboundMessage(Object msg) {
343         if (msg instanceof DatagramPacket) {
344             DatagramPacket packet = (DatagramPacket) msg;
345             checkUnresolved(packet);
346             ByteBuf content = packet.content();
347             return UnixChannelUtil.isBufferCopyNeededForWrite(content) ?
348                     new DatagramPacket(newDirectBuffer(packet, content), packet.recipient()) : msg;
349         }
350 
351         if (msg instanceof ByteBuf) {
352             ByteBuf buf = (ByteBuf) msg;
353             return UnixChannelUtil.isBufferCopyNeededForWrite(buf) ? newDirectBuffer(buf) : buf;
354         }
355 
356         if (msg instanceof AddressedEnvelope) {
357             @SuppressWarnings("unchecked")
358             AddressedEnvelope<Object, SocketAddress> e = (AddressedEnvelope<Object, SocketAddress>) msg;
359             checkUnresolved(e);
360 
361             if (e.content() instanceof ByteBuf &&
362                     (e.recipient() == null || e.recipient() instanceof InetSocketAddress)) {
363 
364                 ByteBuf content = (ByteBuf) e.content();
365                 return UnixChannelUtil.isBufferCopyNeededForWrite(content) ?
366                         new DefaultAddressedEnvelope<ByteBuf, InetSocketAddress>(
367                                 newDirectBuffer(e, content), (InetSocketAddress) e.recipient()) : e;
368             }
369         }
370 
371         throw new UnsupportedOperationException(
372                 "unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
373     }
374 
375     @Override
376     public KQueueDatagramChannelConfig config() {
377         return config;
378     }
379 
380     @Override
381     protected void doDisconnect() throws Exception {
382         socket.disconnect();
383         connected = active = false;
384         resetCachedAddresses();
385     }
386 
387     @Override
388     protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
389         if (super.doConnect(remoteAddress, localAddress)) {
390             connected = true;
391             return true;
392         }
393         return false;
394     }
395 
396     @Override
397     protected void doClose() throws Exception {
398         super.doClose();
399         connected = false;
400     }
401 
402     final class KQueueDatagramChannelUnsafe extends AbstractKQueueUnsafe {
403 
404         @Override
405         void readReady(KQueueRecvByteAllocatorHandle allocHandle) {
406             assert eventLoop().inEventLoop();
407             final DatagramChannelConfig config = config();
408             if (shouldBreakReadReady(config)) {
409                 clearReadFilter0();
410                 return;
411             }
412             final ChannelPipeline pipeline = pipeline();
413             final ByteBufAllocator allocator = config.getAllocator();
414             allocHandle.reset(config);
415 
416             Throwable exception = null;
417             try {
418                 ByteBuf byteBuf = null;
419                 try {
420                     boolean connected = isConnected();
421                     do {
422                         byteBuf = allocHandle.allocate(allocator);
423                         allocHandle.attemptedBytesRead(byteBuf.writableBytes());
424 
425                         final DatagramPacket packet;
426                         if (connected) {
427                             try {
428                                 allocHandle.lastBytesRead(doReadBytes(byteBuf));
429                             } catch (Errors.NativeIoException e) {
430                                 // We need to correctly translate connect errors to match NIO behaviour.
431                                 if (e.expectedErr() == Errors.ERROR_ECONNREFUSED_NEGATIVE) {
432                                     PortUnreachableException error = new PortUnreachableException(e.getMessage());
433                                     error.initCause(e);
434                                     throw error;
435                                 }
436                                 throw e;
437                             }
438                             if (allocHandle.lastBytesRead() <= 0) {
439                                 // nothing was read, release the buffer.
440                                 byteBuf.release();
441                                 byteBuf = null;
442                                 break;
443                             }
444                             packet = new DatagramPacket(byteBuf,
445                                     (InetSocketAddress) localAddress(), (InetSocketAddress) remoteAddress());
446                         } else {
447                             final DatagramSocketAddress remoteAddress;
448                             if (byteBuf.hasMemoryAddress()) {
449                                 // has a memory address so use optimized call
450                                 remoteAddress = socket.recvFromAddress(byteBuf.memoryAddress(), byteBuf.writerIndex(),
451                                         byteBuf.capacity());
452                             } else {
453                                 ByteBuffer nioData = byteBuf.internalNioBuffer(
454                                         byteBuf.writerIndex(), byteBuf.writableBytes());
455                                 remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit());
456                             }
457 
458                             if (remoteAddress == null) {
459                                 allocHandle.lastBytesRead(-1);
460                                 byteBuf.release();
461                                 byteBuf = null;
462                                 break;
463                             }
464                             InetSocketAddress localAddress = remoteAddress.localAddress();
465                             if (localAddress == null) {
466                                 localAddress = (InetSocketAddress) localAddress();
467                             }
468                             allocHandle.lastBytesRead(remoteAddress.receivedAmount());
469                             byteBuf.writerIndex(byteBuf.writerIndex() + allocHandle.lastBytesRead());
470 
471                             packet = new DatagramPacket(byteBuf, localAddress, remoteAddress);
472                         }
473 
474                         allocHandle.incMessagesRead(1);
475 
476                         readPending = false;
477                         pipeline.fireChannelRead(packet);
478 
479                         byteBuf = null;
480 
481                     // We use the TRUE_SUPPLIER as it is also ok to read less then what we did try to read (as long
482                     // as we read anything).
483                     } while (allocHandle.continueReading(UncheckedBooleanSupplier.TRUE_SUPPLIER));
484                 } catch (Throwable t) {
485                     if (byteBuf != null) {
486                         byteBuf.release();
487                     }
488                     exception = t;
489                 }
490 
491                 allocHandle.readComplete();
492                 pipeline.fireChannelReadComplete();
493 
494                 if (exception != null) {
495                     pipeline.fireExceptionCaught(exception);
496                 }
497             } finally {
498                 if (shouldStopReading(config)) {
499                     clearReadFilter0();
500                 }
501             }
502         }
503     }
504 }