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