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