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