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.netty5.channel.epoll;
17  
18  import io.netty5.channel.ChannelException;
19  import io.netty5.channel.DefaultFileRegion;
20  import io.netty5.channel.socket.SocketProtocolFamily;
21  import io.netty5.channel.unix.NativeInetAddress;
22  import io.netty5.channel.unix.PeerCredentials;
23  import io.netty5.channel.unix.Socket;
24  import io.netty5.util.internal.SocketUtils;
25  import io.netty5.util.internal.UnstableApi;
26  
27  import java.io.IOException;
28  import java.net.Inet6Address;
29  import java.net.InetAddress;
30  import java.net.NetworkInterface;
31  import java.net.ProtocolFamily;
32  import java.net.UnknownHostException;
33  import java.util.Enumeration;
34  
35  import static io.netty5.channel.unix.Errors.ioResult;
36  
37  /**
38   * A socket which provides access Linux native methods.
39   */
40  @UnstableApi
41  public final class LinuxSocket extends Socket {
42      static final InetAddress INET6_ANY = unsafeInetAddrByName("::");
43      private static final InetAddress INET_ANY = unsafeInetAddrByName("0.0.0.0");
44      private static final long MAX_UINT32_T = 0xFFFFFFFFL;
45  
46      LinuxSocket(int fd, SocketProtocolFamily family) {
47          super(fd, family);
48      }
49  
50      int sendmmsg(NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
51                                 int offset, int len) throws IOException {
52          return Native.sendmmsg(intValue(), ipv6, msgs, offset, len);
53      }
54  
55      int recvmmsg(NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
56                   int offset, int len) throws IOException {
57          return Native.recvmmsg(intValue(), ipv6, msgs, offset, len);
58      }
59  
60      int recvmsg(NativeDatagramPacketArray.NativeDatagramPacket msg) throws IOException {
61          return Native.recvmsg(intValue(), ipv6, msg);
62      }
63  
64      void setTimeToLive(int ttl) throws IOException {
65          setTimeToLive(intValue(), ttl);
66      }
67  
68      void setInterface(InetAddress address) throws IOException {
69          final NativeInetAddress a = NativeInetAddress.newInstance(address);
70          setInterface(intValue(), ipv6, a.address(), a.scopeId(), interfaceIndex(address));
71      }
72  
73      void setNetworkInterface(NetworkInterface netInterface) throws IOException {
74          InetAddress address = deriveInetAddress(netInterface, protocolFamily() == SocketProtocolFamily.INET6);
75          if (address.equals(protocolFamily() == SocketProtocolFamily.INET ? INET_ANY : INET6_ANY)) {
76              throw new IOException("NetworkInterface does not support " + protocolFamily());
77          }
78          final NativeInetAddress nativeAddress = NativeInetAddress.newInstance(address);
79          setInterface(intValue(), ipv6, nativeAddress.address(), nativeAddress.scopeId(), interfaceIndex(netInterface));
80      }
81  
82      InetAddress getInterface() throws IOException {
83          NetworkInterface inf = getNetworkInterface();
84          if (inf != null) {
85              Enumeration<InetAddress> addresses = SocketUtils.addressesFromNetworkInterface(inf);
86              if (addresses.hasMoreElements()) {
87                  return addresses.nextElement();
88              }
89          }
90          return null;
91      }
92  
93      NetworkInterface getNetworkInterface() throws IOException {
94          int ret = getInterface(intValue(), ipv6);
95          if (ipv6) {
96              return NetworkInterface.getByIndex(ret);
97          }
98          InetAddress address = inetAddress(ret);
99          return address != null ? NetworkInterface.getByInetAddress(address) : null;
100     }
101 
102     private static InetAddress inetAddress(int value) {
103         byte[] var1 = {
104                 (byte) (value >>> 24 & 255),
105                 (byte) (value >>> 16 & 255),
106                 (byte) (value >>> 8 & 255),
107                 (byte) (value & 255)
108         };
109 
110         try {
111             return InetAddress.getByAddress(var1);
112         } catch (UnknownHostException ignore) {
113             return null;
114         }
115     }
116 
117     void joinGroup(InetAddress group, NetworkInterface netInterface, InetAddress source) throws IOException {
118         final NativeInetAddress g = NativeInetAddress.newInstance(group);
119         final boolean isIpv6 = group instanceof Inet6Address;
120         final NativeInetAddress i = NativeInetAddress.newInstance(deriveInetAddress(netInterface, isIpv6));
121         if (source != null) {
122             if (source.getClass() != group.getClass()) {
123                 throw new IllegalArgumentException("Source address is different type to group");
124             }
125             final NativeInetAddress s = NativeInetAddress.newInstance(source);
126             joinSsmGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(),
127                     g.scopeId(), interfaceIndex(netInterface), s.address());
128         } else {
129             joinGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface));
130         }
131     }
132 
133     void leaveGroup(InetAddress group, NetworkInterface netInterface, InetAddress source) throws IOException {
134         final NativeInetAddress g = NativeInetAddress.newInstance(group);
135         final boolean isIpv6 = group instanceof Inet6Address;
136         final NativeInetAddress i = NativeInetAddress.newInstance(deriveInetAddress(netInterface, isIpv6));
137         if (source != null) {
138             if (source.getClass() != group.getClass()) {
139                 throw new IllegalArgumentException("Source address is different type to group");
140             }
141             final NativeInetAddress s = NativeInetAddress.newInstance(source);
142             leaveSsmGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(),
143                     g.scopeId(), interfaceIndex(netInterface), s.address());
144         } else {
145             leaveGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface));
146         }
147     }
148 
149     private static int interfaceIndex(NetworkInterface networkInterface) {
150         return networkInterface.getIndex();
151     }
152 
153     private static int interfaceIndex(InetAddress address) throws IOException {
154         NetworkInterface iface = NetworkInterface.getByInetAddress(address);
155         if (iface != null) {
156             return iface.getIndex();
157         }
158         return -1;
159     }
160 
161     void setTcpDeferAccept(int deferAccept) throws IOException {
162         setTcpDeferAccept(intValue(), deferAccept);
163     }
164 
165     void setTcpQuickAck(boolean quickAck) throws IOException {
166         setTcpQuickAck(intValue(), quickAck ? 1 : 0);
167     }
168 
169     void setTcpCork(boolean tcpCork) throws IOException {
170         setTcpCork(intValue(), tcpCork ? 1 : 0);
171     }
172 
173     void setSoBusyPoll(int loopMicros) throws IOException {
174         setSoBusyPoll(intValue(), loopMicros);
175     }
176 
177     void setTcpNotSentLowAt(long tcpNotSentLowAt) throws IOException {
178         if (tcpNotSentLowAt < 0 || tcpNotSentLowAt > MAX_UINT32_T) {
179             throw new IllegalArgumentException("tcpNotSentLowAt must be a uint32_t");
180         }
181         setTcpNotSentLowAt(intValue(), (int) tcpNotSentLowAt);
182     }
183 
184     void setTcpFastOpen(int tcpFastopenBacklog) throws IOException {
185         setTcpFastOpen(intValue(), tcpFastopenBacklog);
186     }
187 
188     void setTcpKeepIdle(int seconds) throws IOException {
189         setTcpKeepIdle(intValue(), seconds);
190     }
191 
192     void setTcpKeepIntvl(int seconds) throws IOException {
193         setTcpKeepIntvl(intValue(), seconds);
194     }
195 
196     void setTcpKeepCnt(int probes) throws IOException {
197         setTcpKeepCnt(intValue(), probes);
198     }
199 
200     void setTcpUserTimeout(int milliseconds) throws IOException {
201         setTcpUserTimeout(intValue(), milliseconds);
202     }
203 
204     void setIpFreeBind(boolean enabled) throws IOException {
205         setIpFreeBind(intValue(), enabled ? 1 : 0);
206     }
207 
208     void setIpTransparent(boolean enabled) throws IOException {
209         setIpTransparent(intValue(), enabled ? 1 : 0);
210     }
211 
212     void setIpRecvOrigDestAddr(boolean enabled) throws IOException {
213         setIpRecvOrigDestAddr(intValue(), enabled ? 1 : 0);
214     }
215 
216     int getTimeToLive() throws IOException {
217         return getTimeToLive(intValue());
218     }
219 
220     void getTcpInfo(EpollTcpInfo info) throws IOException {
221         getTcpInfo(intValue(), info.info);
222     }
223 
224     void setTcpMd5Sig(InetAddress address, byte[] key) throws IOException {
225         final NativeInetAddress a = NativeInetAddress.newInstance(address);
226         setTcpMd5Sig(intValue(), ipv6, a.address(), a.scopeId(), key);
227     }
228 
229     boolean isTcpCork() throws IOException  {
230         return isTcpCork(intValue()) != 0;
231     }
232 
233     int getSoBusyPoll() throws IOException  {
234         return getSoBusyPoll(intValue());
235     }
236 
237     int getTcpDeferAccept() throws IOException {
238         return getTcpDeferAccept(intValue());
239     }
240 
241     boolean isTcpQuickAck() throws IOException {
242         return isTcpQuickAck(intValue()) != 0;
243     }
244 
245     long getTcpNotSentLowAt() throws IOException {
246         return getTcpNotSentLowAt(intValue()) & MAX_UINT32_T;
247     }
248 
249     int getTcpKeepIdle() throws IOException {
250         return getTcpKeepIdle(intValue());
251     }
252 
253     int getTcpKeepIntvl() throws IOException {
254         return getTcpKeepIntvl(intValue());
255     }
256 
257     int getTcpKeepCnt() throws IOException {
258         return getTcpKeepCnt(intValue());
259     }
260 
261     int getTcpUserTimeout() throws IOException {
262         return getTcpUserTimeout(intValue());
263     }
264 
265     boolean isIpFreeBind() throws IOException {
266         return isIpFreeBind(intValue()) != 0;
267     }
268 
269     boolean isIpTransparent() throws IOException {
270         return isIpTransparent(intValue()) != 0;
271     }
272 
273     boolean isIpRecvOrigDestAddr() throws IOException {
274         return isIpRecvOrigDestAddr(intValue()) != 0;
275     }
276 
277     PeerCredentials getPeerCredentials() throws IOException {
278         return getPeerCredentials(intValue());
279     }
280 
281     boolean isLoopbackModeDisabled() throws IOException {
282         return getIpMulticastLoop(intValue(), ipv6) == 0;
283     }
284 
285     void setLoopbackModeDisabled(boolean loopbackModeDisabled) throws IOException {
286         setIpMulticastLoop(intValue(), ipv6, loopbackModeDisabled ? 0 : 1);
287     }
288 
289     boolean isUdpGro() throws IOException {
290         return isUdpGro(intValue()) != 0;
291     }
292 
293     void setUdpGro(boolean gro) throws IOException {
294         setUdpGro(intValue(), gro ? 1 : 0);
295     }
296 
297     long sendFile(DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException {
298         // Open the file-region as it may be created via the lazy constructor. This is needed as we directly access
299         // the FileChannel field via JNI.
300         src.open();
301 
302         long res = sendFile(intValue(), src, baseOffset, offset, length);
303         if (res >= 0) {
304             return res;
305         }
306         return ioResult("sendfile", (int) res);
307     }
308 
309     private static InetAddress deriveInetAddress(NetworkInterface netInterface, boolean ipv6) {
310         final InetAddress ipAny = ipv6 ? INET6_ANY : INET_ANY;
311         if (netInterface != null) {
312             final Enumeration<InetAddress> ias = netInterface.getInetAddresses();
313             while (ias.hasMoreElements()) {
314                 final InetAddress ia = ias.nextElement();
315                 final boolean isV6 = ia instanceof Inet6Address;
316                 if (isV6 == ipv6) {
317                     return ia;
318                 }
319             }
320         }
321         return ipAny;
322     }
323 
324     public static LinuxSocket newDatagramSocket(ProtocolFamily family) {
325         if (family == null) {
326             return newSocketDgram();
327         }
328         SocketProtocolFamily protocolFamily = SocketProtocolFamily.of(family);
329         switch (protocolFamily) {
330             case UNIX:
331                 return newSocketDomainDgram();
332             case INET6:
333             case INET:
334                 return newSocketDgram(protocolFamily);
335             default:
336                 throw new UnsupportedOperationException();
337         }
338     }
339 
340     public static LinuxSocket newSocket(ProtocolFamily family) {
341         if (family == null) {
342             return newSocketStream();
343         }
344         SocketProtocolFamily protocolFamily = SocketProtocolFamily.of(family);
345 
346         switch (protocolFamily) {
347             case UNIX:
348                 return newSocketDomain();
349             case INET6:
350             case INET:
351                 return newSocketStream(protocolFamily);
352             default:
353                 throw new UnsupportedOperationException();
354         }
355     }
356 
357     public static LinuxSocket newSocketStream(boolean ipv6) {
358         return new LinuxSocket(newSocketStream0(ipv6), ipv6 ? SocketProtocolFamily.INET6 : SocketProtocolFamily.INET);
359     }
360 
361     public static LinuxSocket newSocketStream(ProtocolFamily protocol) {
362         return new LinuxSocket(newSocketStream0(protocol), SocketProtocolFamily.of(protocol));
363     }
364 
365     public static LinuxSocket newSocketStream() {
366         return newSocketStream(isIPv6Preferred());
367     }
368 
369     public static LinuxSocket newSocketDgram(boolean ipv6) {
370         return new LinuxSocket(newSocketDgram0(ipv6), ipv6 ? SocketProtocolFamily.INET6 : SocketProtocolFamily.INET);
371     }
372 
373     public static LinuxSocket newSocketDgram(ProtocolFamily family) {
374         return new LinuxSocket(newSocketDgram0(family), SocketProtocolFamily.of(family));
375     }
376 
377     public static LinuxSocket newSocketDgram() {
378         return newSocketDgram(isIPv6Preferred());
379     }
380 
381     public static LinuxSocket newSocketDomain() {
382         return new LinuxSocket(newSocketDomain0(), SocketProtocolFamily.UNIX);
383     }
384 
385     public static LinuxSocket newSocketDomainDgram() {
386         return new LinuxSocket(newSocketDomainDgram0(), SocketProtocolFamily.UNIX);
387     }
388 
389     private static InetAddress unsafeInetAddrByName(String inetName) {
390         try {
391             return InetAddress.getByName(inetName);
392         } catch (UnknownHostException uhe) {
393             throw new ChannelException(uhe);
394         }
395     }
396 
397     private static native void joinGroup(int fd, boolean ipv6, byte[] group, byte[] interfaceAddress,
398                                          int scopeId, int interfaceIndex) throws IOException;
399     private static native void joinSsmGroup(int fd, boolean ipv6, byte[] group, byte[] interfaceAddress,
400                                             int scopeId, int interfaceIndex, byte[] source) throws IOException;
401     private static native void leaveGroup(int fd, boolean ipv6, byte[] group, byte[] interfaceAddress,
402                                           int scopeId, int interfaceIndex) throws IOException;
403     private static native void leaveSsmGroup(int fd, boolean ipv6, byte[] group, byte[] interfaceAddress,
404                                              int scopeId, int interfaceIndex, byte[] source) throws IOException;
405     private static native long sendFile(int socketFd, DefaultFileRegion src, long baseOffset,
406                                         long offset, long length) throws IOException;
407 
408     private static native int getTcpDeferAccept(int fd) throws IOException;
409     private static native int isTcpQuickAck(int fd) throws IOException;
410     private static native int isTcpCork(int fd) throws IOException;
411     private static native int getSoBusyPoll(int fd) throws IOException;
412     private static native int getTcpNotSentLowAt(int fd) throws IOException;
413     private static native int getTcpKeepIdle(int fd) throws IOException;
414     private static native int getTcpKeepIntvl(int fd) throws IOException;
415     private static native int getTcpKeepCnt(int fd) throws IOException;
416     private static native int getTcpUserTimeout(int fd) throws IOException;
417     private static native int getTimeToLive(int fd) throws IOException;
418     private static native int isIpFreeBind(int fd) throws IOException;
419     private static native int isIpTransparent(int fd) throws IOException;
420     private static native int isIpRecvOrigDestAddr(int fd) throws IOException;
421     private static native void getTcpInfo(int fd, long[] array) throws IOException;
422     private static native PeerCredentials getPeerCredentials(int fd) throws IOException;
423 
424     private static native void setTcpDeferAccept(int fd, int deferAccept) throws IOException;
425     private static native void setTcpQuickAck(int fd, int quickAck) throws IOException;
426     private static native void setTcpCork(int fd, int tcpCork) throws IOException;
427     private static native void setSoBusyPoll(int fd, int loopMicros) throws IOException;
428     private static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException;
429     private static native void setTcpFastOpen(int fd, int tcpFastopenBacklog) throws IOException;
430     private static native void setTcpKeepIdle(int fd, int seconds) throws IOException;
431     private static native void setTcpKeepIntvl(int fd, int seconds) throws IOException;
432     private static native void setTcpKeepCnt(int fd, int probes) throws IOException;
433     private static native void setTcpUserTimeout(int fd, int milliseconds)throws IOException;
434     private static native void setIpFreeBind(int fd, int freeBind) throws IOException;
435     private static native void setIpTransparent(int fd, int transparent) throws IOException;
436     private static native void setIpRecvOrigDestAddr(int fd, int transparent) throws IOException;
437     private static native void setTcpMd5Sig(
438             int fd, boolean ipv6, byte[] address, int scopeId, byte[] key) throws IOException;
439     private static native void setInterface(
440             int fd, boolean ipv6, byte[] interfaceAddress, int scopeId, int networkInterfaceIndex) throws IOException;
441     private static native int getInterface(int fd, boolean ipv6);
442     private static native int getIpMulticastLoop(int fd, boolean ipv6) throws IOException;
443     private static native void setIpMulticastLoop(int fd, boolean ipv6, int enabled) throws IOException;
444     private static native void setTimeToLive(int fd, int ttl) throws IOException;
445     private static native int isUdpGro(int fd) throws IOException;
446     private static native void setUdpGro(int fd, int gro) throws IOException;
447 }