View Javadoc
1   /*
2    * Copyright 2015 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.unix;
17  
18  import io.netty.channel.ChannelException;
19  import io.netty.util.CharsetUtil;
20  import io.netty.util.NetUtil;
21  
22  import java.io.IOException;
23  import java.net.Inet6Address;
24  import java.net.InetAddress;
25  import java.net.InetSocketAddress;
26  import java.net.PortUnreachableException;
27  import java.net.SocketAddress;
28  import java.nio.ByteBuffer;
29  import java.nio.channels.ClosedChannelException;
30  import java.util.concurrent.atomic.AtomicBoolean;
31  
32  import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
33  import static io.netty.channel.unix.Errors.ERROR_ECONNREFUSED_NEGATIVE;
34  import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE;
35  import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
36  import static io.netty.channel.unix.Errors.ioResult;
37  import static io.netty.channel.unix.Errors.newIOException;
38  import static io.netty.channel.unix.Errors.throwConnectException;
39  import static io.netty.channel.unix.LimitsStaticallyReferencedJniMethods.udsSunPathSize;
40  import static io.netty.channel.unix.NativeInetAddress.address;
41  import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
42  import static io.netty.util.internal.ThrowableUtil.unknownStackTrace;
43  
44  /**
45   * Provides a JNI bridge to native socket operations.
46   * <strong>Internal usage only!</strong>
47   */
48  public class Socket extends FileDescriptor {
49      private static final ClosedChannelException SHUTDOWN_CLOSED_CHANNEL_EXCEPTION = unknownStackTrace(
50              new ClosedChannelException(), Socket.class, "shutdown(..)");
51      private static final ClosedChannelException SEND_TO_CLOSED_CHANNEL_EXCEPTION = unknownStackTrace(
52              new ClosedChannelException(), Socket.class, "sendTo(..)");
53      private static final ClosedChannelException SEND_TO_ADDRESS_CLOSED_CHANNEL_EXCEPTION =
54              unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddress(..)");
55      private static final ClosedChannelException SEND_TO_ADDRESSES_CLOSED_CHANNEL_EXCEPTION =
56              unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddresses(..)");
57      private static final Errors.NativeIoException SEND_TO_CONNECTION_RESET_EXCEPTION = unknownStackTrace(
58              Errors.newConnectionResetException("syscall:sendto", Errors.ERRNO_EPIPE_NEGATIVE),
59              Socket.class, "sendTo(..)");
60      private static final Errors.NativeIoException SEND_TO_ADDRESS_CONNECTION_RESET_EXCEPTION =
61              unknownStackTrace(Errors.newConnectionResetException("syscall:sendto",
62                      Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddress");
63      private static final Errors.NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG = unknownStackTrace(
64              Errors.newConnectionResetException("syscall:sendmsg",
65              Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddresses(..)");
66      private static final Errors.NativeIoException CONNECTION_RESET_SHUTDOWN_EXCEPTION =
67              unknownStackTrace(Errors.newConnectionResetException("syscall:shutdown",
68                      Errors.ERRNO_ECONNRESET_NEGATIVE), Socket.class, "shutdown");
69      private static final Errors.NativeConnectException FINISH_CONNECT_REFUSED_EXCEPTION =
70              unknownStackTrace(new Errors.NativeConnectException("syscall:getsockopt",
71                      Errors.ERROR_ECONNREFUSED_NEGATIVE), Socket.class, "finishConnect(..)");
72      private static final Errors.NativeConnectException CONNECT_REFUSED_EXCEPTION =
73              unknownStackTrace(new Errors.NativeConnectException("syscall:connect",
74                      Errors.ERROR_ECONNREFUSED_NEGATIVE), Socket.class, "connect(..)");
75  
76      public static final int UDS_SUN_PATH_SIZE = udsSunPathSize();
77  
78      public Socket(int fd) {
79          super(fd);
80      }
81  
82      public final void shutdown() throws IOException {
83          shutdown(true, true);
84      }
85  
86      public final void shutdown(boolean read, boolean write) throws IOException {
87          for (;;) {
88              // We need to only shutdown what has not been shutdown yet, and if there is no change we should not
89              // shutdown anything. This is because if the underlying FD is reused and we still have an object which
90              // represents the previous incarnation of the FD we need to be sure we don't inadvertently shutdown the
91              // "new" FD without explicitly having a change.
92              final int oldState = this.state;
93              if (isClosed(oldState)) {
94                  throw new ClosedChannelException();
95              }
96              int newState = oldState;
97              if (read && !isInputShutdown(newState)) {
98                  newState = inputShutdown(newState);
99              }
100             if (write && !isOutputShutdown(newState)) {
101                 newState = outputShutdown(newState);
102             }
103 
104             // If there is no change in state, then we should not take any action.
105             if (newState == oldState) {
106                 return;
107             }
108             if (casState(oldState, newState)) {
109                 break;
110             }
111         }
112         int res = shutdown(fd, read, write);
113         if (res < 0) {
114             ioResult("shutdown", res, CONNECTION_RESET_SHUTDOWN_EXCEPTION, SHUTDOWN_CLOSED_CHANNEL_EXCEPTION);
115         }
116     }
117 
118     public final boolean isShutdown() {
119         int state = this.state;
120         return isInputShutdown(state) && isOutputShutdown(state);
121     }
122 
123     public final boolean isInputShutdown() {
124         return isInputShutdown(state);
125     }
126 
127     public final boolean isOutputShutdown() {
128         return isOutputShutdown(state);
129     }
130 
131     public final int sendTo(ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
132         // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
133         // to be called frequently
134         byte[] address;
135         int scopeId;
136         if (addr instanceof Inet6Address) {
137             address = addr.getAddress();
138             scopeId = ((Inet6Address) addr).getScopeId();
139         } else {
140             // convert to ipv4 mapped ipv6 address;
141             scopeId = 0;
142             address = ipv4MappedIpv6Address(addr.getAddress());
143         }
144         int res = sendTo(fd, buf, pos, limit, address, scopeId, port);
145         if (res >= 0) {
146             return res;
147         }
148         if (res == ERROR_ECONNREFUSED_NEGATIVE) {
149             throw new PortUnreachableException("sendTo failed");
150         }
151         return ioResult("sendTo", res, SEND_TO_CONNECTION_RESET_EXCEPTION, SEND_TO_CLOSED_CHANNEL_EXCEPTION);
152     }
153 
154     public final int sendToAddress(long memoryAddress, int pos, int limit, InetAddress addr, int port)
155             throws IOException {
156         // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
157         // to be called frequently
158         byte[] address;
159         int scopeId;
160         if (addr instanceof Inet6Address) {
161             address = addr.getAddress();
162             scopeId = ((Inet6Address) addr).getScopeId();
163         } else {
164             // convert to ipv4 mapped ipv6 address;
165             scopeId = 0;
166             address = ipv4MappedIpv6Address(addr.getAddress());
167         }
168         int res = sendToAddress(fd, memoryAddress, pos, limit, address, scopeId, port);
169         if (res >= 0) {
170             return res;
171         }
172         if (res == ERROR_ECONNREFUSED_NEGATIVE) {
173             throw new PortUnreachableException("sendToAddress failed");
174         }
175         return ioResult("sendToAddress", res,
176                 SEND_TO_ADDRESS_CONNECTION_RESET_EXCEPTION, SEND_TO_ADDRESS_CLOSED_CHANNEL_EXCEPTION);
177     }
178 
179     public final int sendToAddresses(long memoryAddress, int length, InetAddress addr, int port) throws IOException {
180         // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
181         // to be called frequently
182         byte[] address;
183         int scopeId;
184         if (addr instanceof Inet6Address) {
185             address = addr.getAddress();
186             scopeId = ((Inet6Address) addr).getScopeId();
187         } else {
188             // convert to ipv4 mapped ipv6 address;
189             scopeId = 0;
190             address = ipv4MappedIpv6Address(addr.getAddress());
191         }
192         int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port);
193         if (res >= 0) {
194             return res;
195         }
196 
197         if (res == ERROR_ECONNREFUSED_NEGATIVE) {
198             throw new PortUnreachableException("sendToAddresses failed");
199         }
200         return ioResult("sendToAddresses", res,
201                 CONNECTION_RESET_EXCEPTION_SENDMSG, SEND_TO_ADDRESSES_CLOSED_CHANNEL_EXCEPTION);
202     }
203 
204     public final DatagramSocketAddress recvFrom(ByteBuffer buf, int pos, int limit) throws IOException {
205         return recvFrom(fd, buf, pos, limit);
206     }
207 
208     public final DatagramSocketAddress recvFromAddress(long memoryAddress, int pos, int limit) throws IOException {
209         return recvFromAddress(fd, memoryAddress, pos, limit);
210     }
211 
212     public final int recvFd() throws IOException {
213         int res = recvFd(fd);
214         if (res > 0) {
215             return res;
216         }
217         if (res == 0) {
218             return -1;
219         }
220 
221         if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
222             // Everything consumed so just return -1 here.
223             return 0;
224         }
225         throw newIOException("recvFd", res);
226     }
227 
228     public final int sendFd(int fdToSend) throws IOException {
229         int res = sendFd(fd, fdToSend);
230         if (res >= 0) {
231             return res;
232         }
233         if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
234             // Everything consumed so just return -1 here.
235             return -1;
236         }
237         throw newIOException("sendFd", res);
238     }
239 
240     public final boolean connect(SocketAddress socketAddress) throws IOException {
241         int res;
242         if (socketAddress instanceof InetSocketAddress) {
243             InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
244             NativeInetAddress address = NativeInetAddress.newInstance(inetSocketAddress.getAddress());
245             res = connect(fd, address.address, address.scopeId, inetSocketAddress.getPort());
246         } else if (socketAddress instanceof DomainSocketAddress) {
247             DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
248             res = connectDomainSocket(fd, unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8));
249         } else {
250             throw new Error("Unexpected SocketAddress implementation " + socketAddress);
251         }
252         if (res < 0) {
253             if (res == ERRNO_EINPROGRESS_NEGATIVE) {
254                 // connect not complete yet need to wait for EPOLLOUT event
255                 return false;
256             }
257             throwConnectException("connect", CONNECT_REFUSED_EXCEPTION, res);
258         }
259         return true;
260     }
261 
262     public final boolean finishConnect() throws IOException {
263         int res = finishConnect(fd);
264         if (res < 0) {
265             if (res == ERRNO_EINPROGRESS_NEGATIVE) {
266                 // connect still in progress
267                 return false;
268             }
269             throwConnectException("finishConnect", FINISH_CONNECT_REFUSED_EXCEPTION, res);
270         }
271         return true;
272     }
273 
274     public final void disconnect() throws IOException {
275         int res = disconnect(fd);
276         if (res < 0) {
277             throwConnectException("disconnect", FINISH_CONNECT_REFUSED_EXCEPTION, res);
278         }
279     }
280 
281     public final void bind(SocketAddress socketAddress) throws IOException {
282         if (socketAddress instanceof InetSocketAddress) {
283             InetSocketAddress addr = (InetSocketAddress) socketAddress;
284             NativeInetAddress address = NativeInetAddress.newInstance(addr.getAddress());
285             int res = bind(fd, address.address, address.scopeId, addr.getPort());
286             if (res < 0) {
287                 throw newIOException("bind", res);
288             }
289         } else if (socketAddress instanceof DomainSocketAddress) {
290             DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
291             int res = bindDomainSocket(fd, addr.path().getBytes(CharsetUtil.UTF_8));
292             if (res < 0) {
293                 throw newIOException("bind", res);
294             }
295         } else {
296             throw new Error("Unexpected SocketAddress implementation " + socketAddress);
297         }
298     }
299 
300     public final void listen(int backlog) throws IOException {
301         int res = listen(fd, backlog);
302         if (res < 0) {
303             throw newIOException("listen", res);
304         }
305     }
306 
307     public final int accept(byte[] addr) throws IOException {
308         int res = accept(fd, addr);
309         if (res >= 0) {
310             return res;
311         }
312         if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
313             // Everything consumed so just return -1 here.
314             return -1;
315         }
316         throw newIOException("accept", res);
317     }
318 
319     public final InetSocketAddress remoteAddress() {
320         byte[] addr = remoteAddress(fd);
321         // addr may be null if getpeername failed.
322         // See https://github.com/netty/netty/issues/3328
323         return addr == null ? null : address(addr, 0, addr.length);
324     }
325 
326     public final InetSocketAddress localAddress() {
327         byte[] addr = localAddress(fd);
328         // addr may be null if getpeername failed.
329         // See https://github.com/netty/netty/issues/3328
330         return addr == null ? null : address(addr, 0, addr.length);
331     }
332 
333     public final int getReceiveBufferSize() throws IOException {
334         return getReceiveBufferSize(fd);
335     }
336 
337     public final int getSendBufferSize() throws IOException {
338         return getSendBufferSize(fd);
339     }
340 
341     public final boolean isKeepAlive() throws IOException {
342         return isKeepAlive(fd) != 0;
343     }
344 
345     public final boolean isTcpNoDelay() throws IOException {
346         return isTcpNoDelay(fd) != 0;
347     }
348 
349     public final boolean isReuseAddress() throws IOException {
350         return isReuseAddress(fd) != 0;
351     }
352 
353     public final boolean isReusePort() throws IOException {
354         return isReusePort(fd) != 0;
355     }
356 
357     public final boolean isBroadcast() throws IOException {
358         return isBroadcast(fd) != 0;
359     }
360 
361     public final int getSoLinger() throws IOException {
362         return getSoLinger(fd);
363     }
364 
365     public final int getSoError() throws IOException {
366         return getSoError(fd);
367     }
368 
369     public final int getTrafficClass() throws IOException {
370         return getTrafficClass(fd);
371     }
372 
373     public final void setKeepAlive(boolean keepAlive) throws IOException {
374         setKeepAlive(fd, keepAlive ? 1 : 0);
375     }
376 
377     public final void setReceiveBufferSize(int receiveBufferSize) throws IOException  {
378         setReceiveBufferSize(fd, receiveBufferSize);
379     }
380 
381     public final void setSendBufferSize(int sendBufferSize) throws IOException {
382         setSendBufferSize(fd, sendBufferSize);
383     }
384 
385     public final void setTcpNoDelay(boolean tcpNoDelay) throws IOException  {
386         setTcpNoDelay(fd, tcpNoDelay ? 1 : 0);
387     }
388 
389     public final void setSoLinger(int soLinger) throws IOException {
390         setSoLinger(fd, soLinger);
391     }
392 
393     public final void setReuseAddress(boolean reuseAddress) throws IOException {
394         setReuseAddress(fd, reuseAddress ? 1 : 0);
395     }
396 
397     public final void setReusePort(boolean reusePort) throws IOException {
398         setReusePort(fd, reusePort ? 1 : 0);
399     }
400 
401     public final void setBroadcast(boolean broadcast) throws IOException {
402         setBroadcast(fd, broadcast ? 1 : 0);
403     }
404 
405     public final void setTrafficClass(int trafficClass) throws IOException {
406         setTrafficClass(fd, trafficClass);
407     }
408 
409     @Override
410     public String toString() {
411         return "Socket{" +
412                 "fd=" + fd +
413                 '}';
414     }
415 
416     private static final AtomicBoolean INITIALIZED = new AtomicBoolean();
417 
418     public static Socket newSocketStream() {
419         return new Socket(newSocketStream0());
420     }
421 
422     public static Socket newSocketDgram() {
423         return new Socket(newSocketDgram0());
424     }
425 
426     public static Socket newSocketDomain() {
427         return new Socket(newSocketDomain0());
428     }
429 
430     public static void initialize() {
431         if (INITIALIZED.compareAndSet(false, true)) {
432             initialize(NetUtil.isIpV4StackPreferred());
433         }
434     }
435 
436     protected static int newSocketStream0() {
437         int res = newSocketStreamFd();
438         if (res < 0) {
439             throw new ChannelException(newIOException("newSocketStream", res));
440         }
441         return res;
442     }
443 
444     protected static int newSocketDgram0() {
445         int res = newSocketDgramFd();
446         if (res < 0) {
447             throw new ChannelException(newIOException("newSocketDgram", res));
448         }
449         return res;
450     }
451 
452     protected static int newSocketDomain0() {
453         int res = newSocketDomainFd();
454         if (res < 0) {
455             throw new ChannelException(newIOException("newSocketDomain", res));
456         }
457         return res;
458     }
459 
460     private static native int shutdown(int fd, boolean read, boolean write);
461     private static native int connect(int fd, byte[] address, int scopeId, int port);
462     private static native int connectDomainSocket(int fd, byte[] path);
463     private static native int finishConnect(int fd);
464     private static native int disconnect(int fd);
465     private static native int bind(int fd, byte[] address, int scopeId, int port);
466     private static native int bindDomainSocket(int fd, byte[] path);
467     private static native int listen(int fd, int backlog);
468     private static native int accept(int fd, byte[] addr);
469 
470     private static native byte[] remoteAddress(int fd);
471     private static native byte[] localAddress(int fd);
472 
473     private static native int sendTo(
474             int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port);
475     private static native int sendToAddress(
476             int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port);
477     private static native int sendToAddresses(
478             int fd, long memoryAddress, int length, byte[] address, int scopeId, int port);
479 
480     private static native DatagramSocketAddress recvFrom(
481             int fd, ByteBuffer buf, int pos, int limit) throws IOException;
482     private static native DatagramSocketAddress recvFromAddress(
483             int fd, long memoryAddress, int pos, int limit) throws IOException;
484     private static native int recvFd(int fd);
485     private static native int sendFd(int socketFd, int fd);
486 
487     private static native int newSocketStreamFd();
488     private static native int newSocketDgramFd();
489     private static native int newSocketDomainFd();
490 
491     private static native int isReuseAddress(int fd) throws IOException;
492     private static native int isReusePort(int fd) throws IOException;
493     private static native int getReceiveBufferSize(int fd) throws IOException;
494     private static native int getSendBufferSize(int fd) throws IOException;
495     private static native int isKeepAlive(int fd) throws IOException;
496     private static native int isTcpNoDelay(int fd) throws IOException;
497     private static native int isBroadcast(int fd) throws IOException;
498     private static native int getSoLinger(int fd) throws IOException;
499     private static native int getSoError(int fd) throws IOException;
500     private static native int getTrafficClass(int fd) throws IOException;
501 
502     private static native void setReuseAddress(int fd, int reuseAddress) throws IOException;
503     private static native void setReusePort(int fd, int reuseAddress) throws IOException;
504     private static native void setKeepAlive(int fd, int keepAlive) throws IOException;
505     private static native void setReceiveBufferSize(int fd, int receiveBufferSize) throws IOException;
506     private static native void setSendBufferSize(int fd, int sendBufferSize) throws IOException;
507     private static native void setTcpNoDelay(int fd, int tcpNoDelay) throws IOException;
508     private static native void setSoLinger(int fd, int soLinger) throws IOException;
509     private static native void setBroadcast(int fd, int broadcast) throws IOException;
510     private static native void setTrafficClass(int fd, int trafficClass) throws IOException;
511     private static native void initialize(boolean ipv4Preferred);
512 }