1/*
2 * Copyright (c) 2000, 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
26package sun.nio.ch;
27
28import java.io.FileDescriptor;
29import java.io.IOException;
30import java.net.*;
31import java.nio.channels.*;
32import java.nio.channels.spi.*;
33import java.util.*;
34import sun.net.NetHooks;
35
36
37/**
38 * An implementation of ServerSocketChannels
39 */
40
41class ServerSocketChannelImpl
42    extends ServerSocketChannel
43    implements SelChImpl
44{
45
46    // Used to make native close and configure calls
47    private static NativeDispatcher nd;
48
49    // Our file descriptor
50    private final FileDescriptor fd;
51
52    // fd value needed for dev/poll. This value will remain valid
53    // even after the value in the file descriptor object has been set to -1
54    private int fdVal;
55
56    // ID of native thread currently blocked in this channel, for signalling
57    private volatile long thread = 0;
58
59    // Lock held by thread currently blocked in this channel
60    private final Object lock = new Object();
61
62    // Lock held by any thread that modifies the state fields declared below
63    // DO NOT invoke a blocking I/O operation while holding this lock!
64    private final Object stateLock = new Object();
65
66    // -- The following fields are protected by stateLock
67
68    // Channel state, increases monotonically
69    private static final int ST_UNINITIALIZED = -1;
70    private static final int ST_INUSE = 0;
71    private static final int ST_KILLED = 1;
72    private int state = ST_UNINITIALIZED;
73
74    // Binding
75    private InetSocketAddress localAddress; // null => unbound
76
77    // set true when exclusive binding is on and SO_REUSEADDR is emulated
78    private boolean isReuseAddress;
79
80    // Our socket adaptor, if any
81    ServerSocket socket;
82
83    // -- End of fields protected by stateLock
84
85
86    ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
87        super(sp);
88        this.fd =  Net.serverSocket(true);
89        this.fdVal = IOUtil.fdVal(fd);
90        this.state = ST_INUSE;
91    }
92
93    ServerSocketChannelImpl(SelectorProvider sp,
94                            FileDescriptor fd,
95                            boolean bound)
96        throws IOException
97    {
98        super(sp);
99        this.fd =  fd;
100        this.fdVal = IOUtil.fdVal(fd);
101        this.state = ST_INUSE;
102        if (bound)
103            localAddress = Net.localAddress(fd);
104    }
105
106    public ServerSocket socket() {
107        synchronized (stateLock) {
108            if (socket == null)
109                socket = ServerSocketAdaptor.create(this);
110            return socket;
111        }
112    }
113
114    @Override
115    public SocketAddress getLocalAddress() throws IOException {
116        synchronized (stateLock) {
117            if (!isOpen())
118                throw new ClosedChannelException();
119            return localAddress == null ? localAddress
120                    : Net.getRevealedLocalAddress(
121                          Net.asInetSocketAddress(localAddress));
122        }
123    }
124
125    @Override
126    public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
127        throws IOException
128    {
129        if (name == null)
130            throw new NullPointerException();
131        if (!supportedOptions().contains(name))
132            throw new UnsupportedOperationException("'" + name + "' not supported");
133        synchronized (stateLock) {
134            if (!isOpen())
135                throw new ClosedChannelException();
136
137            if (name == StandardSocketOptions.IP_TOS) {
138                ProtocolFamily family = Net.isIPv6Available() ?
139                    StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
140                Net.setSocketOption(fd, family, name, value);
141                return this;
142            }
143
144            if (name == StandardSocketOptions.SO_REUSEADDR &&
145                    Net.useExclusiveBind())
146            {
147                // SO_REUSEADDR emulated when using exclusive bind
148                isReuseAddress = (Boolean)value;
149            } else {
150                // no options that require special handling
151                Net.setSocketOption(fd, Net.UNSPEC, name, value);
152            }
153            return this;
154        }
155    }
156
157    @Override
158    @SuppressWarnings("unchecked")
159    public <T> T getOption(SocketOption<T> name)
160        throws IOException
161    {
162        if (name == null)
163            throw new NullPointerException();
164        if (!supportedOptions().contains(name))
165            throw new UnsupportedOperationException("'" + name + "' not supported");
166
167        synchronized (stateLock) {
168            if (!isOpen())
169                throw new ClosedChannelException();
170            if (name == StandardSocketOptions.SO_REUSEADDR &&
171                    Net.useExclusiveBind())
172            {
173                // SO_REUSEADDR emulated when using exclusive bind
174                return (T)Boolean.valueOf(isReuseAddress);
175            }
176            // no options that require special handling
177            return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
178        }
179    }
180
181    private static class DefaultOptionsHolder {
182        static final Set<SocketOption<?>> defaultOptions = defaultOptions();
183
184        private static Set<SocketOption<?>> defaultOptions() {
185            HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);
186            set.add(StandardSocketOptions.SO_RCVBUF);
187            set.add(StandardSocketOptions.SO_REUSEADDR);
188            set.add(StandardSocketOptions.IP_TOS);
189            return Collections.unmodifiableSet(set);
190        }
191    }
192
193    @Override
194    public final Set<SocketOption<?>> supportedOptions() {
195        return DefaultOptionsHolder.defaultOptions;
196    }
197
198    public boolean isBound() {
199        synchronized (stateLock) {
200            return localAddress != null;
201        }
202    }
203
204    public InetSocketAddress localAddress() {
205        synchronized (stateLock) {
206            return localAddress;
207        }
208    }
209
210    @Override
211    public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
212        synchronized (lock) {
213            if (!isOpen())
214                throw new ClosedChannelException();
215            if (isBound())
216                throw new AlreadyBoundException();
217            InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
218                Net.checkAddress(local);
219            SecurityManager sm = System.getSecurityManager();
220            if (sm != null)
221                sm.checkListen(isa.getPort());
222            NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
223            Net.bind(fd, isa.getAddress(), isa.getPort());
224            Net.listen(fd, backlog < 1 ? 50 : backlog);
225            synchronized (stateLock) {
226                localAddress = Net.localAddress(fd);
227            }
228        }
229        return this;
230    }
231
232    public SocketChannel accept() throws IOException {
233        synchronized (lock) {
234            if (!isOpen())
235                throw new ClosedChannelException();
236            if (!isBound())
237                throw new NotYetBoundException();
238            SocketChannel sc = null;
239
240            int n = 0;
241            FileDescriptor newfd = new FileDescriptor();
242            InetSocketAddress[] isaa = new InetSocketAddress[1];
243
244            try {
245                begin();
246                if (!isOpen())
247                    return null;
248                thread = NativeThread.current();
249                for (;;) {
250                    n = accept(this.fd, newfd, isaa);
251                    if ((n == IOStatus.INTERRUPTED) && isOpen())
252                        continue;
253                    break;
254                }
255            } finally {
256                thread = 0;
257                end(n > 0);
258                assert IOStatus.check(n);
259            }
260
261            if (n < 1)
262                return null;
263
264            IOUtil.configureBlocking(newfd, true);
265            InetSocketAddress isa = isaa[0];
266            sc = new SocketChannelImpl(provider(), newfd, isa);
267            SecurityManager sm = System.getSecurityManager();
268            if (sm != null) {
269                try {
270                    sm.checkAccept(isa.getAddress().getHostAddress(),
271                                   isa.getPort());
272                } catch (SecurityException x) {
273                    sc.close();
274                    throw x;
275                }
276            }
277            return sc;
278
279        }
280    }
281
282    protected void implConfigureBlocking(boolean block) throws IOException {
283        IOUtil.configureBlocking(fd, block);
284    }
285
286    protected void implCloseSelectableChannel() throws IOException {
287        synchronized (stateLock) {
288            if (state != ST_KILLED)
289                nd.preClose(fd);
290            long th = thread;
291            if (th != 0)
292                NativeThread.signal(th);
293            if (!isRegistered())
294                kill();
295        }
296    }
297
298    public void kill() throws IOException {
299        synchronized (stateLock) {
300            if (state == ST_KILLED)
301                return;
302            if (state == ST_UNINITIALIZED) {
303                state = ST_KILLED;
304                return;
305            }
306            assert !isOpen() && !isRegistered();
307            nd.close(fd);
308            state = ST_KILLED;
309        }
310    }
311
312    /**
313     * Translates native poll revent set into a ready operation set
314     */
315    public boolean translateReadyOps(int ops, int initialOps,
316                                     SelectionKeyImpl sk) {
317        int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
318        int oldOps = sk.nioReadyOps();
319        int newOps = initialOps;
320
321        if ((ops & Net.POLLNVAL) != 0) {
322            // This should only happen if this channel is pre-closed while a
323            // selection operation is in progress
324            // ## Throw an error if this channel has not been pre-closed
325            return false;
326        }
327
328        if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
329            newOps = intOps;
330            sk.nioReadyOps(newOps);
331            return (newOps & ~oldOps) != 0;
332        }
333
334        if (((ops & Net.POLLIN) != 0) &&
335            ((intOps & SelectionKey.OP_ACCEPT) != 0))
336                newOps |= SelectionKey.OP_ACCEPT;
337
338        sk.nioReadyOps(newOps);
339        return (newOps & ~oldOps) != 0;
340    }
341
342    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
343        return translateReadyOps(ops, sk.nioReadyOps(), sk);
344    }
345
346    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
347        return translateReadyOps(ops, 0, sk);
348    }
349
350    // package-private
351    int poll(int events, long timeout) throws IOException {
352        assert Thread.holdsLock(blockingLock()) && !isBlocking();
353
354        synchronized (lock) {
355            int n = 0;
356            try {
357                begin();
358                synchronized (stateLock) {
359                    if (!isOpen())
360                        return 0;
361                    thread = NativeThread.current();
362                }
363                n = Net.poll(fd, events, timeout);
364            } finally {
365                thread = 0;
366                end(n > 0);
367            }
368            return n;
369        }
370    }
371
372    /**
373     * Translates an interest operation set into a native poll event set
374     */
375    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
376        int newOps = 0;
377
378        // Translate ops
379        if ((ops & SelectionKey.OP_ACCEPT) != 0)
380            newOps |= Net.POLLIN;
381        // Place ops into pollfd array
382        sk.selector.putEventOps(sk, newOps);
383    }
384
385    public FileDescriptor getFD() {
386        return fd;
387    }
388
389    public int getFDVal() {
390        return fdVal;
391    }
392
393    public String toString() {
394        StringBuffer sb = new StringBuffer();
395        sb.append(this.getClass().getName());
396        sb.append('[');
397        if (!isOpen()) {
398            sb.append("closed");
399        } else {
400            synchronized (stateLock) {
401                InetSocketAddress addr = localAddress();
402                if (addr == null) {
403                    sb.append("unbound");
404                } else {
405                    sb.append(Net.getRevealedLocalAddressAsString(addr));
406                }
407            }
408        }
409        sb.append(']');
410        return sb.toString();
411    }
412
413    /**
414     * Accept a connection on a socket.
415     *
416     * @implNote Wrap native call to allow instrumentation.
417     */
418    private int accept(FileDescriptor ssfd, FileDescriptor newfd,
419                       InetSocketAddress[] isaa)
420        throws IOException
421    {
422        return accept0(ssfd, newfd, isaa);
423    }
424
425    // -- Native methods --
426
427    // Accepts a new connection, setting the given file descriptor to refer to
428    // the new socket and setting isaa[0] to the socket's remote address.
429    // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
430    // connections are pending) or IOStatus.INTERRUPTED.
431    //
432    private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
433                               InetSocketAddress[] isaa)
434        throws IOException;
435
436    private static native void initIDs();
437
438    static {
439        // Android-removed: Code to load native libraries, doesn't make sense on Android.
440        // IOUtil.load();
441        initIDs();
442        nd = new SocketDispatcher();
443    }
444
445}
446