1 /*
2  * Copyright (c) 2008, 2018, 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.nio.channels.*;
29 import java.net.SocketAddress;
30 import java.net.SocketOption;
31 import java.net.StandardSocketOptions;
32 import java.net.InetSocketAddress;
33 import java.io.FileDescriptor;
34 import java.io.IOException;
35 import java.util.Set;
36 import java.util.HashSet;
37 import java.util.Collections;
38 import java.util.concurrent.Future;
39 import java.util.concurrent.locks.ReadWriteLock;
40 import java.util.concurrent.locks.ReentrantReadWriteLock;
41 import sun.net.NetHooks;
42 import sun.net.ext.ExtendedSocketOptions;
43 import static sun.net.ext.ExtendedSocketOptions.SOCK_STREAM;
44 
45 /**
46  * Base implementation of AsynchronousServerSocketChannel.
47  */
48 
49 abstract class AsynchronousServerSocketChannelImpl
50     extends AsynchronousServerSocketChannel
51     implements Cancellable, Groupable
52 {
53     protected final FileDescriptor fd;
54 
55     // the local address to which the channel's socket is bound
56     protected volatile InetSocketAddress localAddress;
57 
58     // need this lock to set local address
59     private final Object stateLock = new Object();
60 
61     // close support
62     private ReadWriteLock closeLock = new ReentrantReadWriteLock();
63     private volatile boolean closed;
64 
65     // set true when accept operation is cancelled
66     private volatile boolean acceptKilled;
67 
68     // set true when exclusive binding is on and SO_REUSEADDR is emulated
69     private boolean isReuseAddress;
70 
AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group)71     AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group) {
72         super(group.provider());
73         this.fd = Net.serverSocket(true);
74     }
75 
76     @Override
isOpen()77     public final boolean isOpen() {
78         return !closed;
79     }
80 
81     /**
82      * Marks beginning of access to file descriptor/handle
83      */
begin()84     final void begin() throws IOException {
85         closeLock.readLock().lock();
86         if (!isOpen())
87             throw new ClosedChannelException();
88     }
89 
90     /**
91      * Marks end of access to file descriptor/handle
92      */
end()93     final void end() {
94         closeLock.readLock().unlock();
95     }
96 
97     /**
98      * Invoked to close file descriptor/handle.
99      */
implClose()100     abstract void implClose() throws IOException;
101 
102     @Override
close()103     public final void close() throws IOException {
104         // synchronize with any threads using file descriptor/handle
105         closeLock.writeLock().lock();
106         try {
107             if (closed)
108                 return;     // already closed
109             closed = true;
110         } finally {
111             closeLock.writeLock().unlock();
112         }
113         implClose();
114     }
115 
116     /**
117      * Invoked by accept to accept connection
118      */
119     abstract Future<AsynchronousSocketChannel>
implAccept(Object attachment, CompletionHandler<AsynchronousSocketChannel,Object> handler)120         implAccept(Object attachment,
121                    CompletionHandler<AsynchronousSocketChannel,Object> handler);
122 
123 
124     @Override
accept()125     public final Future<AsynchronousSocketChannel> accept() {
126         return implAccept(null, null);
127     }
128 
129     @Override
130     @SuppressWarnings("unchecked")
accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler)131     public final <A> void accept(A attachment,
132                                  CompletionHandler<AsynchronousSocketChannel,? super A> handler)
133     {
134         if (handler == null)
135             throw new NullPointerException("'handler' is null");
136         implAccept(attachment, (CompletionHandler<AsynchronousSocketChannel,Object>)handler);
137     }
138 
isAcceptKilled()139     final boolean isAcceptKilled() {
140         return acceptKilled;
141     }
142 
143     @Override
onCancel(PendingFuture<?,?> task)144     public final void onCancel(PendingFuture<?,?> task) {
145         acceptKilled = true;
146     }
147 
148     @Override
bind(SocketAddress local, int backlog)149     public final AsynchronousServerSocketChannel bind(SocketAddress local, int backlog)
150         throws IOException
151     {
152         InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
153             Net.checkAddress(local);
154         SecurityManager sm = System.getSecurityManager();
155         if (sm != null)
156             sm.checkListen(isa.getPort());
157 
158         try {
159             begin();
160             synchronized (stateLock) {
161                 if (localAddress != null)
162                     throw new AlreadyBoundException();
163                 NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
164                 Net.bind(fd, isa.getAddress(), isa.getPort());
165                 Net.listen(fd, backlog < 1 ? 50 : backlog);
166                 localAddress = Net.localAddress(fd);
167             }
168         } finally {
169             end();
170         }
171         return this;
172     }
173 
174     @Override
getLocalAddress()175     public final SocketAddress getLocalAddress() throws IOException {
176         if (!isOpen())
177             throw new ClosedChannelException();
178         return Net.getRevealedLocalAddress(localAddress);
179     }
180 
181     @Override
setOption(SocketOption<T> name, T value)182     public final <T> AsynchronousServerSocketChannel setOption(SocketOption<T> name,
183                                                                T value)
184         throws IOException
185     {
186         if (name == null)
187             throw new NullPointerException();
188         if (!supportedOptions().contains(name))
189             throw new UnsupportedOperationException("'" + name + "' not supported");
190 
191         try {
192             begin();
193             if (name == StandardSocketOptions.SO_REUSEADDR &&
194                     Net.useExclusiveBind())
195             {
196                 // SO_REUSEADDR emulated when using exclusive bind
197                 isReuseAddress = (Boolean)value;
198             } else {
199                 Net.setSocketOption(fd, Net.UNSPEC, name, value);
200             }
201             return this;
202         } finally {
203             end();
204         }
205     }
206 
207     @Override
208     @SuppressWarnings("unchecked")
getOption(SocketOption<T> name)209     public final <T> T getOption(SocketOption<T> name) throws IOException {
210         if (name == null)
211             throw new NullPointerException();
212         if (!supportedOptions().contains(name))
213             throw new UnsupportedOperationException("'" + name + "' not supported");
214 
215         try {
216             begin();
217             if (name == StandardSocketOptions.SO_REUSEADDR &&
218                     Net.useExclusiveBind())
219             {
220                 // SO_REUSEADDR emulated when using exclusive bind
221                 return (T)Boolean.valueOf(isReuseAddress);
222             }
223             return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
224         } finally {
225             end();
226         }
227     }
228 
229     private static class DefaultOptionsHolder {
230         static final Set<SocketOption<?>> defaultOptions = defaultOptions();
231 
defaultOptions()232         private static Set<SocketOption<?>> defaultOptions() {
233             HashSet<SocketOption<?>> set = new HashSet<>(2);
234             set.add(StandardSocketOptions.SO_RCVBUF);
235             set.add(StandardSocketOptions.SO_REUSEADDR);
236             if (Net.isReusePortAvailable()) {
237                 set.add(StandardSocketOptions.SO_REUSEPORT);
238             }
239             set.addAll(ExtendedSocketOptions.options(SOCK_STREAM));
240             return Collections.unmodifiableSet(set);
241         }
242     }
243 
244     @Override
supportedOptions()245     public final Set<SocketOption<?>> supportedOptions() {
246         return DefaultOptionsHolder.defaultOptions;
247     }
248 
249     @Override
toString()250     public final String toString() {
251         StringBuilder sb = new StringBuilder();
252         sb.append(this.getClass().getName());
253         sb.append('[');
254         if (!isOpen())
255             sb.append("closed");
256         else {
257             if (localAddress == null) {
258                 sb.append("unbound");
259             } else {
260                 sb.append(Net.getRevealedLocalAddressAsString(localAddress));
261             }
262         }
263         sb.append(']');
264         return sb.toString();
265     }
266 }
267