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