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