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