1 /* 2 * Copyright (c) 2009, 2011, 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 package sun.nio.ch; 26 27 import java.net.SocketAddress; 28 import java.net.InetSocketAddress; 29 import java.net.InetAddress; 30 import java.io.FileDescriptor; 31 import java.io.IOException; 32 import java.util.Collections; 33 import java.util.Set; 34 import java.util.HashSet; 35 import java.nio.channels.SelectionKey; 36 import java.nio.channels.ClosedChannelException; 37 import java.nio.channels.NotYetBoundException; 38 import java.nio.channels.spi.SelectorProvider; 39 import com.sun.nio.sctp.IllegalUnbindException; 40 import com.sun.nio.sctp.SctpChannel; 41 import com.sun.nio.sctp.SctpServerChannel; 42 import com.sun.nio.sctp.SctpSocketOption; 43 import com.sun.nio.sctp.SctpStandardSocketOptions; 44 45 /** 46 * An implementation of SctpServerChannel 47 */ 48 public class SctpServerChannelImpl extends SctpServerChannel 49 implements SelChImpl 50 { 51 private final FileDescriptor fd; 52 53 private final int fdVal; 54 55 /* IDs of native thread doing accept, for signalling */ 56 private volatile long thread = 0; 57 58 /* Lock held by thread currently blocked in this channel */ 59 private final Object lock = new Object(); 60 61 /* Lock held by any thread that modifies the state fields declared below 62 * DO NOT invoke a blocking I/O operation while holding this lock! */ 63 private final Object stateLock = new Object(); 64 65 private enum ChannelState { 66 UNINITIALIZED, 67 INUSE, 68 KILLPENDING, 69 KILLED, 70 } 71 /* -- The following fields are protected by stateLock -- */ 72 private ChannelState state = ChannelState.UNINITIALIZED; 73 74 /* Binding: Once bound the port will remain constant. */ 75 int port = -1; 76 private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>(); 77 /* Has the channel been bound to the wildcard address */ 78 private boolean wildcard; /* false */ 79 80 /* -- End of fields protected by stateLock -- */ 81 82 /** 83 * Initializes a new instance of this class. 84 */ 85 public SctpServerChannelImpl(SelectorProvider provider) 86 throws IOException { 87 //TODO: update provider remove public modifier 88 super(provider); 89 this.fd = SctpNet.socket(true); 90 this.fdVal = IOUtil.fdVal(fd); 91 this.state = ChannelState.INUSE; 92 } 93 94 @Override 95 public SctpServerChannel bind(SocketAddress local, int backlog) 96 throws IOException { 97 synchronized (lock) { 98 synchronized (stateLock) { 99 if (!isOpen()) 100 throw new ClosedChannelException(); 101 if (isBound()) 102 SctpNet.throwAlreadyBoundException(); 103 104 InetSocketAddress isa = (local == null) ? 105 new InetSocketAddress(0) : Net.checkAddress(local); 106 SecurityManager sm = System.getSecurityManager(); 107 if (sm != null) 108 sm.checkListen(isa.getPort()); 109 Net.bind(fd, isa.getAddress(), isa.getPort()); 110 111 InetSocketAddress boundIsa = Net.localAddress(fd); 112 port = boundIsa.getPort(); 113 localAddresses.add(isa); 114 if (isa.getAddress().isAnyLocalAddress()) 115 wildcard = true; 116 117 SctpNet.listen(fdVal, backlog < 1 ? 50 : backlog); 118 } 119 } 120 return this; 121 } 122 123 @Override 124 public SctpServerChannel bindAddress(InetAddress address) 125 throws IOException { 126 return bindUnbindAddress(address, true); 127 } 128 129 @Override 130 public SctpServerChannel unbindAddress(InetAddress address) 131 throws IOException { 132 return bindUnbindAddress(address, false); 133 } 134 135 private SctpServerChannel bindUnbindAddress(InetAddress address, boolean add) 136 throws IOException { 137 if (address == null) 138 throw new IllegalArgumentException(); 139 140 synchronized (lock) { 141 synchronized (stateLock) { 142 if (!isOpen()) 143 throw new ClosedChannelException(); 144 if (!isBound()) 145 throw new NotYetBoundException(); 146 if (wildcard) 147 throw new IllegalStateException( 148 "Cannot add or remove addresses from a channel that is bound to the wildcard address"); 149 if (address.isAnyLocalAddress()) 150 throw new IllegalArgumentException( 151 "Cannot add or remove the wildcard address"); 152 if (add) { 153 for (InetSocketAddress addr : localAddresses) { 154 if (addr.getAddress().equals(address)) { 155 SctpNet.throwAlreadyBoundException(); 156 } 157 } 158 } else { /*removing */ 159 /* Verify that there is more than one address 160 * and that address is already bound */ 161 if (localAddresses.size() <= 1) 162 throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound"); 163 boolean foundAddress = false; 164 for (InetSocketAddress addr : localAddresses) { 165 if (addr.getAddress().equals(address)) { 166 foundAddress = true; 167 break; 168 } 169 } 170 if (!foundAddress ) 171 throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address"); 172 } 173 174 SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add); 175 176 /* Update our internal Set to reflect the addition/removal */ 177 if (add) 178 localAddresses.add(new InetSocketAddress(address, port)); 179 else { 180 for (InetSocketAddress addr : localAddresses) { 181 if (addr.getAddress().equals(address)) { 182 localAddresses.remove(addr); 183 break; 184 } 185 } 186 } 187 } 188 } 189 return this; 190 } 191 192 private boolean isBound() { 193 synchronized (stateLock) { 194 return port == -1 ? false : true; 195 } 196 } 197 198 private void acceptCleanup() throws IOException { 199 synchronized (stateLock) { 200 thread = 0; 201 if (state == ChannelState.KILLPENDING) 202 kill(); 203 } 204 } 205 206 @Override 207 public SctpChannel accept() throws IOException { 208 synchronized (lock) { 209 if (!isOpen()) 210 throw new ClosedChannelException(); 211 if (!isBound()) 212 throw new NotYetBoundException(); 213 SctpChannel sc = null; 214 215 int n = 0; 216 FileDescriptor newfd = new FileDescriptor(); 217 InetSocketAddress[] isaa = new InetSocketAddress[1]; 218 219 try { 220 begin(); 221 if (!isOpen()) 222 return null; 223 thread = NativeThread.current(); 224 for (;;) { 225 n = accept0(fd, newfd, isaa); 226 if ((n == IOStatus.INTERRUPTED) && isOpen()) 227 continue; 228 break; 229 } 230 } finally { 231 acceptCleanup(); 232 end(n > 0); 233 assert IOStatus.check(n); 234 } 235 236 if (n < 1) 237 return null; 238 239 IOUtil.configureBlocking(newfd, true); 240 InetSocketAddress isa = isaa[0]; 241 sc = new SctpChannelImpl(provider(), newfd); 242 243 SecurityManager sm = System.getSecurityManager(); 244 if (sm != null) 245 sm.checkAccept(isa.getAddress().getHostAddress(), 246 isa.getPort()); 247 248 return sc; 249 } 250 } 251 252 @Override 253 protected void implConfigureBlocking(boolean block) throws IOException { 254 IOUtil.configureBlocking(fd, block); 255 } 256 257 @Override 258 public void implCloseSelectableChannel() throws IOException { 259 synchronized (stateLock) { 260 SctpNet.preClose(fdVal); 261 if (thread != 0) 262 NativeThread.signal(thread); 263 if (!isRegistered()) 264 kill(); 265 } 266 } 267 268 @Override 269 public void kill() throws IOException { 270 synchronized (stateLock) { 271 if (state == ChannelState.KILLED) 272 return; 273 if (state == ChannelState.UNINITIALIZED) { 274 state = ChannelState.KILLED; 275 return; 276 } 277 assert !isOpen() && !isRegistered(); 278 279 // Postpone the kill if there is a thread in accept 280 if (thread == 0) { 281 SctpNet.close(fdVal); 282 state = ChannelState.KILLED; 283 } else { 284 state = ChannelState.KILLPENDING; 285 } 286 } 287 } 288 289 @Override 290 public FileDescriptor getFD() { 291 return fd; 292 } 293 294 @Override 295 public int getFDVal() { 296 return fdVal; 297 } 298 299 /** 300 * Translates native poll revent ops into a ready operation ops 301 */ 302 private boolean translateReadyOps(int ops, int initialOps, 303 SelectionKeyImpl sk) { 304 int intOps = sk.nioInterestOps(); 305 int oldOps = sk.nioReadyOps(); 306 int newOps = initialOps; 307 308 if ((ops & PollArrayWrapper.POLLNVAL) != 0) { 309 /* This should only happen if this channel is pre-closed while a 310 * selection operation is in progress 311 * ## Throw an error if this channel has not been pre-closed */ 312 return false; 313 } 314 315 if ((ops & (PollArrayWrapper.POLLERR 316 | PollArrayWrapper.POLLHUP)) != 0) { 317 newOps = intOps; 318 sk.nioReadyOps(newOps); 319 return (newOps & ~oldOps) != 0; 320 } 321 322 if (((ops & PollArrayWrapper.POLLIN) != 0) && 323 ((intOps & SelectionKey.OP_ACCEPT) != 0)) 324 newOps |= SelectionKey.OP_ACCEPT; 325 326 sk.nioReadyOps(newOps); 327 return (newOps & ~oldOps) != 0; 328 } 329 330 @Override 331 public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { 332 return translateReadyOps(ops, sk.nioReadyOps(), sk); 333 } 334 335 @Override 336 public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { 337 return translateReadyOps(ops, 0, sk); 338 } 339 340 @Override 341 public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { 342 int newOps = 0; 343 344 /* Translate ops */ 345 if ((ops & SelectionKey.OP_ACCEPT) != 0) 346 newOps |= PollArrayWrapper.POLLIN; 347 /* Place ops into pollfd array */ 348 sk.selector.putEventOps(sk, newOps); 349 350 } 351 352 @Override 353 public <T> SctpServerChannel setOption(SctpSocketOption<T> name, T value) 354 throws IOException { 355 if (name == null) 356 throw new NullPointerException(); 357 if (!supportedOptions().contains(name)) 358 throw new UnsupportedOperationException("'" + name + "' not supported"); 359 360 synchronized (stateLock) { 361 if (!isOpen()) 362 throw new ClosedChannelException(); 363 364 SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/); 365 return this; 366 } 367 } 368 369 @Override 370 @SuppressWarnings("unchecked") 371 public <T> T getOption(SctpSocketOption<T> name) throws IOException { 372 if (name == null) 373 throw new NullPointerException(); 374 if (!supportedOptions().contains(name)) 375 throw new UnsupportedOperationException("'" + name + "' not supported"); 376 377 synchronized (stateLock) { 378 if (!isOpen()) 379 throw new ClosedChannelException(); 380 381 return (T) SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/); 382 } 383 } 384 385 private static class DefaultOptionsHolder { 386 static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions(); 387 388 private static Set<SctpSocketOption<?>> defaultOptions() { 389 HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(1); 390 set.add(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS); 391 return Collections.unmodifiableSet(set); 392 } 393 } 394 395 @Override 396 public final Set<SctpSocketOption<?>> supportedOptions() { 397 return DefaultOptionsHolder.defaultOptions; 398 } 399 400 @Override 401 public Set<SocketAddress> getAllLocalAddresses() 402 throws IOException { 403 synchronized (stateLock) { 404 if (!isOpen()) 405 throw new ClosedChannelException(); 406 if (!isBound()) 407 return Collections.emptySet(); 408 409 return SctpNet.getLocalAddresses(fdVal); 410 } 411 } 412 413 /* Native */ 414 private static native void initIDs(); 415 416 private static native int accept0(FileDescriptor ssfd, 417 FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException; 418 419 static { 420 Util.load(); // loads nio & net native libraries 421 java.security.AccessController.doPrivileged( 422 new sun.security.action.LoadLibraryAction("sctp")); 423 initIDs(); 424 } 425 }