1 /*
   2  * Copyright (c) 2000, 2019, 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.ServerSocket;
  32 import java.net.SocketAddress;
  33 import java.net.SocketOption;
  34 import java.net.SocketTimeoutException;
  35 import java.net.StandardSocketOptions;
  36 import java.nio.channels.AlreadyBoundException;
  37 import java.nio.channels.AsynchronousCloseException;
  38 import java.nio.channels.ClosedChannelException;
  39 import java.nio.channels.IllegalBlockingModeException;
  40 import java.nio.channels.NotYetBoundException;
  41 import java.nio.channels.SelectionKey;
  42 import java.nio.channels.ServerSocketChannel;
  43 import java.nio.channels.SocketChannel;
  44 import java.nio.channels.spi.SelectorProvider;
  45 import java.util.Collections;
  46 import java.util.HashSet;
  47 import java.util.Objects;
  48 import java.util.Set;
  49 import java.util.concurrent.locks.ReentrantLock;
  50 
  51 import sun.net.NetHooks;
  52 import sun.net.ext.ExtendedSocketOptions;
  53 
  54 /**
  55  * An implementation of ServerSocketChannels
  56  */
  57 
  58 class ServerSocketChannelImpl
  59     extends ServerSocketChannel
  60     implements SelChImpl
  61 {
  62     // Used to make native close and configure calls
  63     private static final NativeDispatcher nd = new SocketDispatcher();
  64 
  65     // Our file descriptor
  66     private final FileDescriptor fd;
  67     private final int fdVal;
  68 
  69     // Lock held by thread currently blocked on this channel
  70     private final ReentrantLock acceptLock = new ReentrantLock();
  71 
  72     // Lock held by any thread that modifies the state fields declared below
  73     // DO NOT invoke a blocking I/O operation while holding this lock!
  74     private final Object stateLock = new Object();
  75 
  76     // -- The following fields are protected by stateLock
  77 
  78     // Channel state, increases monotonically
  79     private static final int ST_INUSE = 0;
  80     private static final int ST_CLOSING = 1;
  81     private static final int ST_CLOSED = 2;
  82     private int state;
  83 
  84     // ID of native thread currently blocked in this channel, for signalling
  85     private long thread;
  86 
  87     // Binding
  88     private InetSocketAddress localAddress; // null => unbound
  89 
  90     // set true when exclusive binding is on and SO_REUSEADDR is emulated
  91     private boolean isReuseAddress;
  92 
  93     // Our socket adaptor, if any
  94     private ServerSocket socket;
  95 
  96     // -- End of fields protected by stateLock
  97 
  98 
  99     ServerSocketChannelImpl(SelectorProvider sp) {
 100         super(sp);
 101         this.fd = Net.serverSocket(true);
 102         this.fdVal = IOUtil.fdVal(fd);
 103     }
 104 
 105     ServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound)
 106         throws IOException
 107     {
 108         super(sp);
 109         this.fd =  fd;
 110         this.fdVal = IOUtil.fdVal(fd);
 111         if (bound) {
 112             synchronized (stateLock) {
 113                 localAddress = Net.localAddress(fd);
 114             }
 115         }
 116     }
 117 
 118     // @throws ClosedChannelException if channel is closed
 119     private void ensureOpen() throws ClosedChannelException {
 120         if (!isOpen())
 121             throw new ClosedChannelException();
 122     }
 123 
 124     @Override
 125     public ServerSocket socket() {
 126         synchronized (stateLock) {
 127             if (socket == null)
 128                 socket = ServerSocketAdaptor.create(this);
 129             return socket;
 130         }
 131     }
 132 
 133     @Override
 134     public SocketAddress getLocalAddress() throws IOException {
 135         synchronized (stateLock) {
 136             ensureOpen();
 137             return (localAddress == null)
 138                     ? null
 139                     : Net.getRevealedLocalAddress(localAddress);
 140         }
 141     }
 142 
 143     @Override
 144     public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
 145         throws IOException
 146     {
 147         Objects.requireNonNull(name);
 148         if (!supportedOptions().contains(name))
 149             throw new UnsupportedOperationException("'" + name + "' not supported");
 150         synchronized (stateLock) {
 151             ensureOpen();
 152 
 153             if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
 154                 // SO_REUSEADDR emulated when using exclusive bind
 155                 isReuseAddress = (Boolean)value;
 156             } else {
 157                 // no options that require special handling
 158                 Net.setSocketOption(fd, Net.UNSPEC, name, value);
 159             }
 160             return this;
 161         }
 162     }
 163 
 164     @Override
 165     @SuppressWarnings("unchecked")
 166     public <T> T getOption(SocketOption<T> name)
 167         throws IOException
 168     {
 169         Objects.requireNonNull(name);
 170         if (!supportedOptions().contains(name))
 171             throw new UnsupportedOperationException("'" + name + "' not supported");
 172 
 173         synchronized (stateLock) {
 174             ensureOpen();
 175             if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
 176                 // SO_REUSEADDR emulated when using exclusive bind
 177                 return (T)Boolean.valueOf(isReuseAddress);
 178             }
 179             // no options that require special handling
 180             return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
 181         }
 182     }
 183 
 184     private static class DefaultOptionsHolder {
 185         static final Set<SocketOption<?>> defaultOptions = defaultOptions();
 186 
 187         private static Set<SocketOption<?>> defaultOptions() {
 188             HashSet<SocketOption<?>> set = new HashSet<>();
 189             set.add(StandardSocketOptions.SO_RCVBUF);
 190             set.add(StandardSocketOptions.SO_REUSEADDR);
 191             if (Net.isReusePortAvailable()) {
 192                 set.add(StandardSocketOptions.SO_REUSEPORT);
 193             }
 194             set.addAll(ExtendedSocketOptions.serverSocketOptions());
 195             return Collections.unmodifiableSet(set);
 196         }
 197     }
 198 
 199     @Override
 200     public final Set<SocketOption<?>> supportedOptions() {
 201         return DefaultOptionsHolder.defaultOptions;
 202     }
 203 
 204     @Override
 205     public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
 206         synchronized (stateLock) {
 207             ensureOpen();
 208             if (localAddress != null)
 209                 throw new AlreadyBoundException();
 210             InetSocketAddress isa = (local == null)
 211                                     ? new InetSocketAddress(0)
 212                                     : Net.checkAddress(local);
 213             SecurityManager sm = System.getSecurityManager();
 214             if (sm != null)
 215                 sm.checkListen(isa.getPort());
 216             NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
 217             Net.bind(fd, isa.getAddress(), isa.getPort());
 218             Net.listen(fd, backlog < 1 ? 50 : backlog);
 219             localAddress = Net.localAddress(fd);
 220         }
 221         return this;
 222     }
 223 
 224     /**
 225      * Marks the beginning of an I/O operation that might block.
 226      *
 227      * @throws ClosedChannelException if the channel is closed
 228      * @throws NotYetBoundException if the channel's socket has not been bound yet
 229      */
 230     private void begin(boolean blocking) throws ClosedChannelException {
 231         if (blocking)
 232             begin();  // set blocker to close channel if interrupted
 233         synchronized (stateLock) {
 234             ensureOpen();
 235             if (localAddress == null)
 236                 throw new NotYetBoundException();
 237             if (blocking)
 238                 thread = NativeThread.current();
 239         }
 240     }
 241 
 242     /**
 243      * Marks the end of an I/O operation that may have blocked.
 244      *
 245      * @throws AsynchronousCloseException if the channel was closed due to this
 246      * thread being interrupted on a blocking I/O operation.
 247      */
 248     private void end(boolean blocking, boolean completed)
 249         throws AsynchronousCloseException
 250     {
 251         if (blocking) {
 252             synchronized (stateLock) {
 253                 thread = 0;
 254                 if (state == ST_CLOSING) {
 255                     tryFinishClose();
 256                 }
 257             }
 258             end(completed);
 259         }
 260     }
 261 
 262     @Override
 263     public SocketChannel accept() throws IOException {
 264         int n = 0;
 265         FileDescriptor newfd = new FileDescriptor();
 266         InetSocketAddress[] isaa = new InetSocketAddress[1];
 267 
 268         acceptLock.lock();
 269         try {
 270             boolean blocking = isBlocking();
 271             try {
 272                 begin(blocking);
 273                 n = Net.accept(this.fd, newfd, isaa);
 274                 if (blocking) {
 275                     while (IOStatus.okayToRetry(n) && isOpen()) {
 276                         park(Net.POLLIN);
 277                         n = Net.accept(this.fd, newfd, isaa);
 278                     }
 279                 }
 280             } finally {
 281                 end(blocking, n > 0);
 282                 assert IOStatus.check(n);
 283             }
 284         } finally {
 285             acceptLock.unlock();
 286         }
 287 
 288         if (n > 0) {
 289             return finishAccept(newfd, isaa[0]);
 290         } else {
 291             return null;
 292         }
 293     }
 294 
 295     /**
 296      * Accepts a new connection with a given timeout. This method requires the
 297      * channel to be configured in blocking mode.
 298      *
 299      * @apiNote This method is for use by the socket adaptor.
 300      *
 301      * @param nanos the timeout, in nanoseconds
 302      * @throws IllegalBlockingModeException if the channel is configured non-blocking
 303      * @throws SocketTimeoutException if the timeout expires
 304      */
 305     SocketChannel blockingAccept(long nanos) throws IOException {
 306         int n = 0;
 307         FileDescriptor newfd = new FileDescriptor();
 308         InetSocketAddress[] isaa = new InetSocketAddress[1];
 309 
 310         acceptLock.lock();
 311         try {
 312             // check that channel is configured blocking
 313             if (!isBlocking())
 314                 throw new IllegalBlockingModeException();
 315 
 316             try {
 317                 begin(true);
 318                 // change socket to non-blocking
 319                 lockedConfigureBlocking(false);
 320                 try {
 321                     long startNanos = System.nanoTime();
 322                     n = Net.accept(fd, newfd, isaa);
 323                     while (n == IOStatus.UNAVAILABLE && isOpen()) {
 324                         long remainingNanos = nanos - (System.nanoTime() - startNanos);
 325                         if (remainingNanos <= 0) {
 326                             throw new SocketTimeoutException("Accept timed out");
 327                         }
 328                         park(Net.POLLIN, remainingNanos);
 329                         n = Net.accept(fd, newfd, isaa);
 330                     }
 331                 } finally {
 332                     // restore socket to blocking mode
 333                     lockedConfigureBlocking(true);
 334                 }
 335             } finally {
 336                 end(true, n > 0);
 337             }
 338         } finally {
 339             acceptLock.unlock();
 340         }
 341 
 342         assert n > 0;
 343         return finishAccept(newfd, isaa[0]);
 344     }
 345 
 346     private SocketChannel finishAccept(FileDescriptor newfd, InetSocketAddress isa)
 347         throws IOException
 348     {
 349         try {
 350             // newly accepted socket is initially in blocking mode
 351             IOUtil.configureBlocking(newfd, true);
 352 
 353             // check permitted to accept connections from the remote address
 354             SecurityManager sm = System.getSecurityManager();
 355             if (sm != null) {
 356                 sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
 357             }
 358             return new SocketChannelImpl(provider(), newfd, isa);
 359         } catch (Exception e) {
 360             nd.close(newfd);
 361             throw e;
 362         }
 363     }
 364 
 365     @Override
 366     protected void implConfigureBlocking(boolean block) throws IOException {
 367         acceptLock.lock();
 368         try {
 369             lockedConfigureBlocking(block);
 370         } finally {
 371             acceptLock.unlock();
 372         }
 373     }
 374 
 375     /**
 376      * Adjust the blocking mode while holding acceptLock.
 377      */
 378     private void lockedConfigureBlocking(boolean block) throws IOException {
 379         assert acceptLock.isHeldByCurrentThread();
 380         synchronized (stateLock) {
 381             ensureOpen();
 382             IOUtil.configureBlocking(fd, block);
 383         }
 384     }
 385 
 386     /**
 387      * Closes the socket if there are no accept in progress and the channel is
 388      * not registered with a Selector.
 389      */
 390     private boolean tryClose() throws IOException {
 391         assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
 392         if ((thread == 0) && !isRegistered()) {
 393             state = ST_CLOSED;
 394             nd.close(fd);
 395             return true;
 396         } else {
 397             return false;
 398         }
 399     }
 400 
 401     /**
 402      * Invokes tryClose to attempt to close the socket.
 403      *
 404      * This method is used for deferred closing by I/O and Selector operations.
 405      */
 406     private void tryFinishClose() {
 407         try {
 408             tryClose();
 409         } catch (IOException ignore) { }
 410     }
 411 
 412     /**
 413      * Closes this channel when configured in blocking mode.
 414      *
 415      * If there is an accept in progress then the socket is pre-closed and the
 416      * accept thread is signalled, in which case the final close is deferred
 417      * until the accept aborts.
 418      */
 419     private void implCloseBlockingMode() throws IOException {
 420         synchronized (stateLock) {
 421             assert state < ST_CLOSING;
 422             state = ST_CLOSING;
 423             if (!tryClose()) {
 424                 long th = thread;
 425                 if (th != 0) {
 426                     nd.preClose(fd);
 427                     NativeThread.signal(th);
 428                 }
 429             }
 430         }
 431     }
 432 
 433     /**
 434      * Closes this channel when configured in non-blocking mode.
 435      *
 436      * If the channel is registered with a Selector then the close is deferred
 437      * until the channel is flushed from all Selectors.
 438      */
 439     private void implCloseNonBlockingMode() throws IOException {
 440         synchronized (stateLock) {
 441             assert state < ST_CLOSING;
 442             state = ST_CLOSING;
 443         }
 444         // wait for any accept to complete before trying to close
 445         acceptLock.lock();
 446         acceptLock.unlock();
 447         synchronized (stateLock) {
 448             if (state == ST_CLOSING) {
 449                 tryClose();
 450             }
 451         }
 452     }
 453 
 454     /**
 455      * Invoked by implCloseChannel to close the channel.
 456      */
 457     @Override
 458     protected void implCloseSelectableChannel() throws IOException {
 459         assert !isOpen();
 460         if (isBlocking()) {
 461             implCloseBlockingMode();
 462         } else {
 463             implCloseNonBlockingMode();
 464         }
 465     }
 466 
 467     @Override
 468     public void kill() {
 469         synchronized (stateLock) {
 470             if (state == ST_CLOSING) {
 471                 tryFinishClose();
 472             }
 473         }
 474     }
 475 
 476     /**
 477      * Returns true if channel's socket is bound
 478      */
 479     boolean isBound() {
 480         synchronized (stateLock) {
 481             return localAddress != null;
 482         }
 483     }
 484 
 485     /**
 486      * Returns the local address, or null if not bound
 487      */
 488     InetSocketAddress localAddress() {
 489         synchronized (stateLock) {
 490             return localAddress;
 491         }
 492     }
 493 
 494     /**
 495      * Translates native poll revent set into a ready operation set
 496      */
 497     public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {
 498         int intOps = ski.nioInterestOps();
 499         int oldOps = ski.nioReadyOps();
 500         int newOps = initialOps;
 501 
 502         if ((ops & Net.POLLNVAL) != 0) {
 503             // This should only happen if this channel is pre-closed while a
 504             // selection operation is in progress
 505             // ## Throw an error if this channel has not been pre-closed
 506             return false;
 507         }
 508 
 509         if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
 510             newOps = intOps;
 511             ski.nioReadyOps(newOps);
 512             return (newOps & ~oldOps) != 0;
 513         }
 514 
 515         if (((ops & Net.POLLIN) != 0) &&
 516             ((intOps & SelectionKey.OP_ACCEPT) != 0))
 517                 newOps |= SelectionKey.OP_ACCEPT;
 518 
 519         ski.nioReadyOps(newOps);
 520         return (newOps & ~oldOps) != 0;
 521     }
 522 
 523     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
 524         return translateReadyOps(ops, ski.nioReadyOps(), ski);
 525     }
 526 
 527     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
 528         return translateReadyOps(ops, 0, ski);
 529     }
 530 
 531     /**
 532      * Translates an interest operation set into a native poll event set
 533      */
 534     public int translateInterestOps(int ops) {
 535         int newOps = 0;
 536         if ((ops & SelectionKey.OP_ACCEPT) != 0)
 537             newOps |= Net.POLLIN;
 538         return newOps;
 539     }
 540 
 541     public FileDescriptor getFD() {
 542         return fd;
 543     }
 544 
 545     public int getFDVal() {
 546         return fdVal;
 547     }
 548 
 549     public String toString() {
 550         StringBuilder sb = new StringBuilder();
 551         sb.append(this.getClass().getName());
 552         sb.append('[');
 553         if (!isOpen()) {
 554             sb.append("closed");
 555         } else {
 556             synchronized (stateLock) {
 557                 InetSocketAddress addr = localAddress;
 558                 if (addr == null) {
 559                     sb.append("unbound");
 560                 } else {
 561                     sb.append(Net.getRevealedLocalAddressAsString(addr));
 562                 }
 563             }
 564         }
 565         sb.append(']');
 566         return sb.toString();
 567     }
 568 }