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