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