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  *   http://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.epoll;
17
18 import io.netty.channel.ChannelException;
19 import io.netty.channel.DefaultFileRegion;
20 import io.netty.channel.unix.NativeInetAddress;
21 import io.netty.channel.unix.PeerCredentials;
22 import io.netty.channel.unix.Socket;
23 import io.netty.channel.socket.InternetProtocolFamily;
24 import io.netty.util.internal.PlatformDependent;
25 import io.netty.util.internal.SocketUtils;
26
27 import java.io.IOException;
28 import java.net.InetAddress;
29 import java.net.Inet6Address;
30 import java.net.NetworkInterface;
31 import java.net.UnknownHostException;
32 import java.util.Enumeration;
33
34 import static io.netty.channel.unix.Errors.ioResult;
35
36 /**
37  * A socket which provides access Linux native methods.
38  */

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