1 /* 2 * Copyright (c) 1996, 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 java.net; 26 27 import java.io.FileDescriptor; 28 import java.io.IOException; 29 import java.util.Collections; 30 import java.util.HashSet; 31 import java.util.Objects; 32 import java.util.Set; 33 34 import sun.net.ResourceManager; 35 import sun.net.ext.ExtendedSocketOptions; 36 import sun.net.util.IPAddressUtil; 37 import sun.security.action.GetPropertyAction; 38 39 /** 40 * Abstract datagram and multicast socket implementation base class. 41 * Note: This is not a public class, so that applets cannot call 42 * into the implementation directly and hence cannot bypass the 43 * security checks present in the DatagramSocket and MulticastSocket 44 * classes. 45 * 46 * @author Pavani Diwanji 47 */ 48 49 abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl 50 { 51 /* timeout value for receive() */ 52 int timeout = 0; 53 boolean connected = false; 54 private int trafficClass = 0; 55 protected InetAddress connectedAddress = null; 56 private int connectedPort = -1; 57 58 private static final String os = 59 GetPropertyAction.privilegedGetProperty("os.name"); 60 61 /** 62 * flag set if the native connect() call not to be used 63 */ 64 private static final boolean connectDisabled = os.contains("OS X"); 65 66 /** 67 * Load net library into runtime. 68 */ 69 static { 70 jdk.internal.loader.BootLoader.loadLibrary("net"); 71 } 72 73 private static volatile boolean checkedReusePort; 74 private static volatile boolean isReusePortAvailable; 75 76 /** 77 * Tells whether SO_REUSEPORT is supported. 78 */ 79 static boolean isReusePortAvailable() { 80 if (!checkedReusePort) { 81 isReusePortAvailable = isReusePortAvailable0(); 82 checkedReusePort = true; 83 } 84 return isReusePortAvailable; 85 } 86 87 /** 88 * Creates a datagram socket 89 */ 90 protected synchronized void create() throws SocketException { 91 ResourceManager.beforeUdpCreate(); 92 fd = new FileDescriptor(); 93 try { 94 datagramSocketCreate(); 95 SocketCleanable.register(fd); 96 } catch (SocketException ioe) { 97 ResourceManager.afterUdpClose(); 98 fd = null; 99 throw ioe; 100 } 101 } 102 103 /** 104 * Binds a datagram socket to a local port. 105 */ 106 protected synchronized void bind(int lport, InetAddress laddr) 107 throws SocketException { 108 if (laddr.isLinkLocalAddress()) { 109 laddr = IPAddressUtil.toScopedAddress(laddr); 110 } 111 bind0(lport, laddr); 112 } 113 114 protected abstract void bind0(int lport, InetAddress laddr) 115 throws SocketException; 116 117 /** 118 * Sends a datagram packet. The packet contains the data and the 119 * destination address to send the packet to. 120 * @param p the packet to be sent. 121 */ 122 protected void send(DatagramPacket p) throws IOException { 123 InetAddress orig = p.getAddress(); 124 if (orig.isLinkLocalAddress()) { 125 InetAddress scoped = IPAddressUtil.toScopedAddress(orig); 126 if (orig != scoped) { 127 p = new DatagramPacket(p.getData(), p.getOffset(), 128 p.getLength(), scoped, p.getPort()); 129 } 130 } 131 send0(p); 132 } 133 134 protected abstract void send0(DatagramPacket p) throws IOException; 135 136 /** 137 * Connects a datagram socket to a remote destination. This associates the remote 138 * address with the local socket so that datagrams may only be sent to this destination 139 * and received from this destination. 140 * @param address the remote InetAddress to connect to 141 * @param port the remote port number 142 */ 143 protected void connect(InetAddress address, int port) throws SocketException { 144 if (address.isLinkLocalAddress()) { 145 address = IPAddressUtil.toScopedAddress(address); 146 } 147 connect0(address, port); 148 connectedAddress = address; 149 connectedPort = port; 150 connected = true; 151 } 152 153 /** 154 * Disconnects a previously connected socket. Does nothing if the socket was 155 * not connected already. 156 */ 157 protected void disconnect() { 158 disconnect0(connectedAddress.holder().getFamily()); 159 connected = false; 160 connectedAddress = null; 161 connectedPort = -1; 162 } 163 164 /** 165 * Peek at the packet to see who it is from. 166 * @param i the address to populate with the sender address 167 */ 168 protected abstract int peek(InetAddress i) throws IOException; 169 protected abstract int peekData(DatagramPacket p) throws IOException; 170 /** 171 * Receive the datagram packet. 172 * @param p the packet to receive into 173 */ 174 protected synchronized void receive(DatagramPacket p) 175 throws IOException { 176 receive0(p); 177 } 178 179 protected abstract void receive0(DatagramPacket p) 180 throws IOException; 181 182 /** 183 * Set the TTL (time-to-live) option. 184 * @param ttl TTL to be set. 185 */ 186 protected abstract void setTimeToLive(int ttl) throws IOException; 187 188 /** 189 * Get the TTL (time-to-live) option. 190 */ 191 protected abstract int getTimeToLive() throws IOException; 192 193 /** 194 * Set the TTL (time-to-live) option. 195 * @param ttl TTL to be set. 196 */ 197 @Deprecated 198 protected abstract void setTTL(byte ttl) throws IOException; 199 200 /** 201 * Get the TTL (time-to-live) option. 202 */ 203 @Deprecated 204 protected abstract byte getTTL() throws IOException; 205 206 /** 207 * Join the multicast group. 208 * @param inetaddr multicast address to join. 209 */ 210 protected void join(InetAddress inetaddr) throws IOException { 211 join(inetaddr, null); 212 } 213 214 /** 215 * Leave the multicast group. 216 * @param inetaddr multicast address to leave. 217 */ 218 protected void leave(InetAddress inetaddr) throws IOException { 219 leave(inetaddr, null); 220 } 221 /** 222 * Join the multicast group. 223 * @param mcastaddr multicast address to join. 224 * @param netIf specifies the local interface to receive multicast 225 * datagram packets 226 * @throws IllegalArgumentException if mcastaddr is null or is a 227 * SocketAddress subclass not supported by this socket 228 * @since 1.4 229 */ 230 231 protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) 232 throws IOException { 233 if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) 234 throw new IllegalArgumentException("Unsupported address type"); 235 join(((InetSocketAddress)mcastaddr).getAddress(), netIf); 236 } 237 238 protected abstract void join(InetAddress inetaddr, NetworkInterface netIf) 239 throws IOException; 240 241 /** 242 * Leave the multicast group. 243 * @param mcastaddr multicast address to leave. 244 * @param netIf specified the local interface to leave the group at 245 * @throws IllegalArgumentException if mcastaddr is null or is a 246 * SocketAddress subclass not supported by this socket 247 * @since 1.4 248 */ 249 protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) 250 throws IOException { 251 if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) 252 throw new IllegalArgumentException("Unsupported address type"); 253 leave(((InetSocketAddress)mcastaddr).getAddress(), netIf); 254 } 255 256 protected abstract void leave(InetAddress inetaddr, NetworkInterface netIf) 257 throws IOException; 258 259 /** 260 * Close the socket. 261 */ 262 protected void close() { 263 if (fd != null) { 264 SocketCleanable.unregister(fd); 265 datagramSocketClose(); 266 ResourceManager.afterUdpClose(); 267 fd = null; 268 } 269 } 270 271 protected boolean isClosed() { 272 return (fd == null) ? true : false; 273 } 274 275 /** 276 * set a value - since we only support (setting) binary options 277 * here, o must be a Boolean 278 */ 279 280 public void setOption(int optID, Object o) throws SocketException { 281 if (isClosed()) { 282 throw new SocketException("Socket Closed"); 283 } 284 switch (optID) { 285 /* check type safety b4 going native. These should never 286 * fail, since only java.Socket* has access to 287 * PlainSocketImpl.setOption(). 288 */ 289 case SO_TIMEOUT: 290 if (o == null || !(o instanceof Integer)) { 291 throw new SocketException("bad argument for SO_TIMEOUT"); 292 } 293 int tmp = ((Integer) o).intValue(); 294 if (tmp < 0) 295 throw new IllegalArgumentException("timeout < 0"); 296 timeout = tmp; 297 return; 298 case IP_TOS: 299 if (o == null || !(o instanceof Integer)) { 300 throw new SocketException("bad argument for IP_TOS"); 301 } 302 trafficClass = ((Integer)o).intValue(); 303 break; 304 case SO_REUSEADDR: 305 if (o == null || !(o instanceof Boolean)) { 306 throw new SocketException("bad argument for SO_REUSEADDR"); 307 } 308 break; 309 case SO_BROADCAST: 310 if (o == null || !(o instanceof Boolean)) { 311 throw new SocketException("bad argument for SO_BROADCAST"); 312 } 313 break; 314 case SO_BINDADDR: 315 throw new SocketException("Cannot re-bind Socket"); 316 case SO_RCVBUF: 317 case SO_SNDBUF: 318 if (o == null || !(o instanceof Integer) || 319 ((Integer)o).intValue() < 0) { 320 throw new SocketException("bad argument for SO_SNDBUF or " + 321 "SO_RCVBUF"); 322 } 323 break; 324 case IP_MULTICAST_IF: 325 if (o == null || !(o instanceof InetAddress)) 326 throw new SocketException("bad argument for IP_MULTICAST_IF"); 327 break; 328 case IP_MULTICAST_IF2: 329 if (o == null || !(o instanceof NetworkInterface)) 330 throw new SocketException("bad argument for IP_MULTICAST_IF2"); 331 break; 332 case IP_MULTICAST_LOOP: 333 if (o == null || !(o instanceof Boolean)) 334 throw new SocketException("bad argument for IP_MULTICAST_LOOP"); 335 break; 336 case SO_REUSEPORT: 337 if (o == null || !(o instanceof Boolean)) { 338 throw new SocketException("bad argument for SO_REUSEPORT"); 339 } 340 if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { 341 throw new UnsupportedOperationException("unsupported option"); 342 } 343 break; 344 default: 345 throw new SocketException("invalid option: " + optID); 346 } 347 socketSetOption(optID, o); 348 } 349 350 /* 351 * get option's state - set or not 352 */ 353 354 public Object getOption(int optID) throws SocketException { 355 if (isClosed()) { 356 throw new SocketException("Socket Closed"); 357 } 358 359 Object result; 360 361 switch (optID) { 362 case SO_TIMEOUT: 363 result = timeout; 364 break; 365 366 case IP_TOS: 367 result = socketGetOption(optID); 368 if ( ((Integer)result).intValue() == -1) { 369 result = trafficClass; 370 } 371 break; 372 373 case SO_BINDADDR: 374 case IP_MULTICAST_IF: 375 case IP_MULTICAST_IF2: 376 case SO_RCVBUF: 377 case SO_SNDBUF: 378 case IP_MULTICAST_LOOP: 379 case SO_REUSEADDR: 380 case SO_BROADCAST: 381 result = socketGetOption(optID); 382 break; 383 384 case SO_REUSEPORT: 385 if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { 386 throw new UnsupportedOperationException("unsupported option"); 387 } 388 result = socketGetOption(optID); 389 break; 390 391 default: 392 throw new SocketException("invalid option: " + optID); 393 } 394 395 return result; 396 } 397 398 static final ExtendedSocketOptions extendedOptions = 399 ExtendedSocketOptions.getInstance(); 400 401 private static final Set<SocketOption<?>> datagramSocketOptions = datagramSocketOptions(); 402 private static final Set<SocketOption<?>> multicastSocketOptions = multicastSocketOptions(); 403 404 private static Set<SocketOption<?>> datagramSocketOptions() { 405 HashSet<SocketOption<?>> options = new HashSet<>(); 406 options.add(StandardSocketOptions.SO_SNDBUF); 407 options.add(StandardSocketOptions.SO_RCVBUF); 408 options.add(StandardSocketOptions.SO_REUSEADDR); 409 options.add(StandardSocketOptions.IP_TOS); 410 if (isReusePortAvailable()) 411 options.add(StandardSocketOptions.SO_REUSEPORT); 412 options.addAll(ExtendedSocketOptions.datagramSocketOptions()); 413 return Collections.unmodifiableSet(options); 414 } 415 416 private static Set<SocketOption<?>> multicastSocketOptions() { 417 HashSet<SocketOption<?>> options = new HashSet<>(); 418 options.add(StandardSocketOptions.SO_SNDBUF); 419 options.add(StandardSocketOptions.SO_RCVBUF); 420 options.add(StandardSocketOptions.SO_REUSEADDR); 421 options.add(StandardSocketOptions.IP_TOS); 422 options.add(StandardSocketOptions.IP_MULTICAST_IF); 423 options.add(StandardSocketOptions.IP_MULTICAST_TTL); 424 options.add(StandardSocketOptions.IP_MULTICAST_LOOP); 425 if (isReusePortAvailable()) 426 options.add(StandardSocketOptions.SO_REUSEPORT); 427 options.addAll(ExtendedSocketOptions.datagramSocketOptions()); 428 return Collections.unmodifiableSet(options); 429 } 430 431 @Override 432 protected Set<SocketOption<?>> supportedOptions() { 433 if (getDatagramSocket() instanceof MulticastSocket) 434 return multicastSocketOptions; 435 else 436 return datagramSocketOptions; 437 } 438 439 @Override 440 protected <T> void setOption(SocketOption<T> name, T value) throws IOException { 441 Objects.requireNonNull(name); 442 if (!supportedOptions().contains(name)) 443 throw new UnsupportedOperationException("'" + name + "' not supported"); 444 445 if (!name.type().isInstance(value)) 446 throw new IllegalArgumentException("Invalid value '" + value + "'"); 447 448 if (isClosed()) 449 throw new SocketException("Socket closed"); 450 451 if (name == StandardSocketOptions.SO_SNDBUF) { 452 if (((Integer)value).intValue() < 0) 453 throw new IllegalArgumentException("Invalid send buffer size:" + value); 454 setOption(SocketOptions.SO_SNDBUF, value); 455 } else if (name == StandardSocketOptions.SO_RCVBUF) { 456 if (((Integer)value).intValue() < 0) 457 throw new IllegalArgumentException("Invalid recv buffer size:" + value); 458 setOption(SocketOptions.SO_RCVBUF, value); 459 } else if (name == StandardSocketOptions.SO_REUSEADDR) { 460 setOption(SocketOptions.SO_REUSEADDR, value); 461 } else if (name == StandardSocketOptions.SO_REUSEPORT) { 462 setOption(SocketOptions.SO_REUSEPORT, value); 463 } else if (name == StandardSocketOptions.IP_TOS) { 464 int i = ((Integer)value).intValue(); 465 if (i < 0 || i > 255) 466 throw new IllegalArgumentException("Invalid IP_TOS value: " + value); 467 setOption(SocketOptions.IP_TOS, value); 468 } else if (name == StandardSocketOptions.IP_MULTICAST_IF ) { 469 setOption(SocketOptions.IP_MULTICAST_IF2, value); 470 } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) { 471 int i = ((Integer)value).intValue(); 472 if (i < 0 || i > 255) 473 throw new IllegalArgumentException("Invalid TTL/hop value: " + value); 474 setTimeToLive((Integer)value); 475 } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) { 476 setOption(SocketOptions.IP_MULTICAST_LOOP, value); 477 } else if (extendedOptions.isOptionSupported(name)) { 478 extendedOptions.setOption(fd, name, value); 479 } else { 480 throw new AssertionError("unknown option :" + name); 481 } 482 } 483 484 @Override 485 @SuppressWarnings("unchecked") 486 protected <T> T getOption(SocketOption<T> name) throws IOException { 487 Objects.requireNonNull(name); 488 if (!supportedOptions().contains(name)) 489 throw new UnsupportedOperationException("'" + name + "' not supported"); 490 491 if (isClosed()) 492 throw new SocketException("Socket closed"); 493 494 if (name == StandardSocketOptions.SO_SNDBUF) { 495 return (T) getOption(SocketOptions.SO_SNDBUF); 496 } else if (name == StandardSocketOptions.SO_RCVBUF) { 497 return (T) getOption(SocketOptions.SO_RCVBUF); 498 } else if (name == StandardSocketOptions.SO_REUSEADDR) { 499 return (T) getOption(SocketOptions.SO_REUSEADDR); 500 } else if (name == StandardSocketOptions.SO_REUSEPORT) { 501 return (T) getOption(SocketOptions.SO_REUSEPORT); 502 } else if (name == StandardSocketOptions.IP_TOS) { 503 return (T) getOption(SocketOptions.IP_TOS); 504 } else if (name == StandardSocketOptions.IP_MULTICAST_IF) { 505 return (T) getOption(SocketOptions.IP_MULTICAST_IF2); 506 } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) { 507 return (T) ((Integer) getTimeToLive()); 508 } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) { 509 return (T) getOption(SocketOptions.IP_MULTICAST_LOOP); 510 } else if (extendedOptions.isOptionSupported(name)) { 511 return (T) extendedOptions.getOption(fd, name); 512 } else { 513 throw new AssertionError("unknown option: " + name); 514 } 515 } 516 517 protected abstract void datagramSocketCreate() throws SocketException; 518 protected abstract void datagramSocketClose(); 519 protected abstract void socketSetOption(int opt, Object val) 520 throws SocketException; 521 protected abstract Object socketGetOption(int opt) throws SocketException; 522 523 protected abstract void connect0(InetAddress address, int port) throws SocketException; 524 protected abstract void disconnect0(int family); 525 526 protected boolean nativeConnectDisabled() { 527 return connectDisabled; 528 } 529 530 abstract int dataAvailable(); 531 private static native boolean isReusePortAvailable0(); 532 }