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