1 /*
2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.nio.ch;
27 
28 import java.io.FileDescriptor;
29 import java.io.IOException;
30 import java.net.InetSocketAddress;
31 import java.nio.channels.AcceptPendingException;
32 import java.nio.channels.AsynchronousCloseException;
33 import java.nio.channels.AsynchronousSocketChannel;
34 import java.nio.channels.ClosedChannelException;
35 import java.nio.channels.CompletionHandler;
36 import java.nio.channels.NotYetBoundException;
37 import java.security.AccessControlContext;
38 import java.security.AccessController;
39 import java.security.PrivilegedAction;
40 import java.util.concurrent.Future;
41 import java.util.concurrent.atomic.AtomicBoolean;
42 
43 import dalvik.system.CloseGuard;
44 
45 /**
46  * Unix implementation of AsynchronousServerSocketChannel
47  */
48 
49 class UnixAsynchronousServerSocketChannelImpl
50     extends AsynchronousServerSocketChannelImpl
51     implements Port.PollableChannel
52 {
53     private final static NativeDispatcher nd = new SocketDispatcher();
54 
55     private final Port port;
56     private final int fdVal;
57 
58     // flag to indicate an accept is outstanding
59     private final AtomicBoolean accepting = new AtomicBoolean();
enableAccept()60     private void enableAccept() {
61         accepting.set(false);
62     }
63 
64     // used to ensure that the context for an asynchronous accept is visible
65     // the pooled thread that handles the I/O event
66     private final Object updateLock = new Object();
67 
68     // pending accept
69     private boolean acceptPending;
70     private CompletionHandler<AsynchronousSocketChannel,Object> acceptHandler;
71     private Object acceptAttachment;
72     private PendingFuture<AsynchronousSocketChannel,Object> acceptFuture;
73 
74     // context for permission check when security manager set
75     private AccessControlContext acceptAcc;
76 
77     // Android-changed: Add CloseGuard support.
78     private final CloseGuard guard = CloseGuard.get();
79 
80 
UnixAsynchronousServerSocketChannelImpl(Port port)81     UnixAsynchronousServerSocketChannelImpl(Port port)
82         throws IOException
83     {
84         super(port);
85 
86         try {
87             IOUtil.configureBlocking(fd, false);
88         } catch (IOException x) {
89             nd.close(fd);  // prevent leak
90             throw x;
91         }
92         this.port = port;
93         this.fdVal = IOUtil.fdVal(fd);
94 
95         // add mapping from file descriptor to this channel
96         port.register(fdVal, this);
97         // Android-changed: Add CloseGuard support.
98         guard.open("close");
99     }
100 
101     @Override
implClose()102     void implClose() throws IOException {
103         // Android-changed: Add CloseGuard support.
104         guard.close();
105         // remove the mapping
106         port.unregister(fdVal);
107 
108         // close file descriptor
109         nd.close(fd);
110 
111         // if there is a pending accept then complete it
112         CompletionHandler<AsynchronousSocketChannel,Object> handler;
113         Object att;
114         PendingFuture<AsynchronousSocketChannel,Object> future;
115         synchronized (updateLock) {
116             if (!acceptPending)
117                 return;  // no pending accept
118             acceptPending = false;
119             handler = acceptHandler;
120             att = acceptAttachment;
121             future = acceptFuture;
122         }
123 
124         // discard the stack trace as otherwise it may appear that implClose
125         // has thrown the exception.
126         AsynchronousCloseException x = new AsynchronousCloseException();
127         x.setStackTrace(new StackTraceElement[0]);
128         if (handler == null) {
129             future.setFailure(x);
130         } else {
131             // invoke by submitting task rather than directly
132             Invoker.invokeIndirectly(this, handler, att, null, x);
133         }
134     }
135 
finalize()136     protected void finalize() throws Throwable {
137         try {
138             if (guard != null) {
139                 guard.warnIfOpen();
140             }
141             close();
142         } finally {
143             super.finalize();
144         }
145     }
146 
147     @Override
group()148     public AsynchronousChannelGroupImpl group() {
149         return port;
150     }
151 
152     /**
153      * Invoked by event handling thread when listener socket is polled
154      */
155     @Override
onEvent(int events, boolean mayInvokeDirect)156     public void onEvent(int events, boolean mayInvokeDirect) {
157         synchronized (updateLock) {
158             if (!acceptPending)
159                 return;  // may have been grabbed by asynchronous close
160             acceptPending = false;
161         }
162 
163         // attempt to accept connection
164         FileDescriptor newfd = new FileDescriptor();
165         InetSocketAddress[] isaa = new InetSocketAddress[1];
166         Throwable exc = null;
167         try {
168             begin();
169             int n = accept(this.fd, newfd, isaa);
170 
171             // spurious wakeup, is this possible?
172             if (n == IOStatus.UNAVAILABLE) {
173                 synchronized (updateLock) {
174                     acceptPending = true;
175                 }
176                 port.startPoll(fdVal, Net.POLLIN);
177                 return;
178             }
179 
180         } catch (Throwable x) {
181             if (x instanceof ClosedChannelException)
182                 x = new AsynchronousCloseException();
183             exc = x;
184         } finally {
185             end();
186         }
187 
188         // Connection accepted so finish it when not holding locks.
189         AsynchronousSocketChannel child = null;
190         if (exc == null) {
191             try {
192                 child = finishAccept(newfd, isaa[0], acceptAcc);
193             } catch (Throwable x) {
194                 if (!(x instanceof IOException) && !(x instanceof SecurityException))
195                     x = new IOException(x);
196                 exc = x;
197             }
198         }
199 
200         // copy field befores accept is re-renabled
201         CompletionHandler<AsynchronousSocketChannel,Object> handler = acceptHandler;
202         Object att = acceptAttachment;
203         PendingFuture<AsynchronousSocketChannel,Object> future = acceptFuture;
204 
205         // re-enable accepting and invoke handler
206         enableAccept();
207 
208         if (handler == null) {
209             future.setResult(child, exc);
210             // if an async cancel has already cancelled the operation then
211             // close the new channel so as to free resources
212             if (child != null && future.isCancelled()) {
213                 try {
214                     child.close();
215                 } catch (IOException ignore) { }
216             }
217         } else {
218             Invoker.invoke(this, handler, att, child, exc);
219         }
220     }
221 
222     /**
223      * Completes the accept by creating the AsynchronousSocketChannel for
224      * the given file descriptor and remote address. If this method completes
225      * with an IOException or SecurityException then the channel/file descriptor
226      * will be closed.
227      */
finishAccept(FileDescriptor newfd, final InetSocketAddress remote, AccessControlContext acc)228     private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,
229                                                    final InetSocketAddress remote,
230                                                    AccessControlContext acc)
231         throws IOException, SecurityException
232     {
233         AsynchronousSocketChannel ch = null;
234         try {
235             ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);
236         } catch (IOException x) {
237             nd.close(newfd);
238             throw x;
239         }
240 
241         // permission check must always be in initiator's context
242         try {
243             if (acc != null) {
244                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
245                     public Void run() {
246                         SecurityManager sm = System.getSecurityManager();
247                         if (sm != null) {
248                             sm.checkAccept(remote.getAddress().getHostAddress(),
249                                            remote.getPort());
250                         }
251                         return null;
252                     }
253                 }, acc);
254             } else {
255                 SecurityManager sm = System.getSecurityManager();
256                 if (sm != null) {
257                     sm.checkAccept(remote.getAddress().getHostAddress(),
258                                    remote.getPort());
259                 }
260             }
261         } catch (SecurityException x) {
262             try {
263                 ch.close();
264             } catch (Throwable suppressed) {
265                 x.addSuppressed(suppressed);
266             }
267             throw x;
268         }
269         return ch;
270     }
271 
272     @Override
implAccept(Object att, CompletionHandler<AsynchronousSocketChannel,Object> handler)273     Future<AsynchronousSocketChannel> implAccept(Object att,
274         CompletionHandler<AsynchronousSocketChannel,Object> handler)
275     {
276         // complete immediately if channel is closed
277         if (!isOpen()) {
278             Throwable e = new ClosedChannelException();
279             if (handler == null) {
280                 return CompletedFuture.withFailure(e);
281             } else {
282                 Invoker.invoke(this, handler, att, null, e);
283                 return null;
284             }
285         }
286         if (localAddress == null)
287             throw new NotYetBoundException();
288 
289         // cancel was invoked with pending accept so connection may have been
290         // dropped.
291         if (isAcceptKilled())
292             throw new RuntimeException("Accept not allowed due cancellation");
293 
294         // check and set flag to prevent concurrent accepting
295         if (!accepting.compareAndSet(false, true))
296             throw new AcceptPendingException();
297 
298         // attempt accept
299         FileDescriptor newfd = new FileDescriptor();
300         InetSocketAddress[] isaa = new InetSocketAddress[1];
301         Throwable exc = null;
302         try {
303             begin();
304 
305             int n = accept(this.fd, newfd, isaa);
306             if (n == IOStatus.UNAVAILABLE) {
307 
308                 // need calling context when there is security manager as
309                 // permission check may be done in a different thread without
310                 // any application call frames on the stack
311                 PendingFuture<AsynchronousSocketChannel,Object> result = null;
312                 synchronized (updateLock) {
313                     if (handler == null) {
314                         this.acceptHandler = null;
315                         result = new PendingFuture<AsynchronousSocketChannel,Object>(this);
316                         this.acceptFuture = result;
317                     } else {
318                         this.acceptHandler = handler;
319                         this.acceptAttachment = att;
320                     }
321                     this.acceptAcc = (System.getSecurityManager() == null) ?
322                         null : AccessController.getContext();
323                     this.acceptPending = true;
324                 }
325 
326                 // register for connections
327                 port.startPoll(fdVal, Net.POLLIN);
328                 return result;
329             }
330         } catch (Throwable x) {
331             // accept failed
332             if (x instanceof ClosedChannelException)
333                 x = new AsynchronousCloseException();
334             exc = x;
335         } finally {
336             end();
337         }
338 
339         AsynchronousSocketChannel child = null;
340         if (exc == null) {
341             // connection accepted immediately
342             try {
343                 child = finishAccept(newfd, isaa[0], null);
344             } catch (Throwable x) {
345                 exc = x;
346             }
347         }
348 
349         // re-enable accepting before invoking handler
350         enableAccept();
351 
352         if (handler == null) {
353             return CompletedFuture.withResult(child, exc);
354         } else {
355             Invoker.invokeIndirectly(this, handler, att, child, exc);
356             return null;
357         }
358     }
359 
360     /**
361      * Accept a connection on a socket.
362      *
363      * @implNote Wrap native call to allow instrumentation.
364      */
accept(FileDescriptor ssfd, FileDescriptor newfd, InetSocketAddress[] isaa)365     private int accept(FileDescriptor ssfd, FileDescriptor newfd,
366                        InetSocketAddress[] isaa)
367         throws IOException
368     {
369         return accept0(ssfd, newfd, isaa);
370     }
371 
372     // -- Native methods --
373 
initIDs()374     private static native void initIDs();
375 
376     // Accepts a new connection, setting the given file descriptor to refer to
377     // the new socket and setting isaa[0] to the socket's remote address.
378     // Returns 1 on success, or IOStatus.UNAVAILABLE.
379     //
accept0(FileDescriptor ssfd, FileDescriptor newfd, InetSocketAddress[] isaa)380     private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
381                                InetSocketAddress[] isaa)
382         throws IOException;
383 
384     static {
initIDs()385         initIDs();
386     }
387 }
388