1 /* 2 * Copyright (c) 2001, 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 26 package sun.nio.ch; 27 28 import java.io.IOException; 29 import java.lang.invoke.MethodHandle; 30 import java.lang.invoke.MethodHandles; 31 import java.lang.invoke.MethodHandles.Lookup; 32 import java.lang.invoke.MethodType; 33 import java.lang.invoke.VarHandle; 34 import java.net.DatagramPacket; 35 import java.net.DatagramSocket; 36 import java.net.InetAddress; 37 import java.net.InetSocketAddress; 38 import java.net.NetworkInterface; 39 import java.net.MulticastSocket; 40 import java.net.SocketAddress; 41 import java.net.SocketException; 42 import java.net.SocketOption; 43 import java.net.StandardSocketOptions; 44 import java.nio.ByteBuffer; 45 import java.nio.channels.AlreadyConnectedException; 46 import java.nio.channels.ClosedChannelException; 47 import java.nio.channels.DatagramChannel; 48 import java.nio.channels.MembershipKey; 49 import java.security.AccessController; 50 import java.security.PrivilegedAction; 51 import java.security.PrivilegedExceptionAction; 52 import java.util.Objects; 53 import java.util.Set; 54 import java.util.concurrent.locks.ReentrantLock; 55 56 import static java.util.concurrent.TimeUnit.MILLISECONDS; 57 58 /** 59 * A multicast datagram socket based on a datagram channel. 60 * 61 * This class overrides every public method defined by java.net.DatagramSocket 62 * and java.net.MulticastSocket. The methods in this class are defined in exactly 63 * the same order as in java.net.DatagramSocket and java.net.MulticastSocket so 64 * as to simplify tracking changes. 65 */ 66 public class DatagramSocketAdaptor 67 extends MulticastSocket 68 { 69 // The channel being adapted 70 private final DatagramChannelImpl dc; 71 72 // Timeout "option" value for receives 73 private volatile int timeout; 74 75 private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException { 76 super(/*SocketAddress*/null); 77 this.dc = dc; 78 } 79 80 static DatagramSocket create(DatagramChannelImpl dc) { 81 try { 82 return new DatagramSocketAdaptor(dc); 83 } catch (IOException e) { 84 throw new Error(e); 85 } 86 } 87 88 private void connectInternal(SocketAddress remote) throws SocketException { 89 try { 90 dc.connect(remote, false); // skips check for already connected 91 } catch (ClosedChannelException e) { 92 // ignore 93 } catch (Exception x) { 94 Net.translateToSocketException(x); 95 } 96 } 97 98 @Override 99 public void bind(SocketAddress local) throws SocketException { 100 if (local != null) { 101 local = Net.asInetSocketAddress(local); 102 } else { 103 local = new InetSocketAddress(0); 104 } 105 try { 106 dc.bind(local); 107 } catch (Exception x) { 108 Net.translateToSocketException(x); 109 } 110 } 111 112 @Override 113 public void connect(InetAddress address, int port) { 114 if (address == null) 115 throw new IllegalArgumentException("Address can't be null"); 116 try { 117 connectInternal(new InetSocketAddress(address, port)); 118 } catch (SocketException x) { 119 throw new Error(x); 120 } 121 } 122 123 @Override 124 public void connect(SocketAddress remote) throws SocketException { 125 if (remote == null) 126 throw new IllegalArgumentException("Address can't be null"); 127 connectInternal(Net.asInetSocketAddress(remote)); 128 } 129 130 @Override 131 public void disconnect() { 132 try { 133 dc.disconnect(); 134 } catch (IOException x) { 135 throw new Error(x); 136 } 137 } 138 139 @Override 140 public boolean isBound() { 141 return dc.localAddress() != null; 142 } 143 144 @Override 145 public boolean isConnected() { 146 return dc.remoteAddress() != null; 147 } 148 149 @Override 150 public InetAddress getInetAddress() { 151 InetSocketAddress remote = dc.remoteAddress(); 152 return (remote != null) ? remote.getAddress() : null; 153 } 154 155 @Override 156 public int getPort() { 157 InetSocketAddress remote = dc.remoteAddress(); 158 return (remote != null) ? remote.getPort() : -1; 159 } 160 161 @Override 162 public SocketAddress getRemoteSocketAddress() { 163 return dc.remoteAddress(); 164 } 165 166 @Override 167 public SocketAddress getLocalSocketAddress() { 168 try { 169 return dc.getLocalAddress(); 170 } catch (ClosedChannelException e) { 171 return null; 172 } catch (Exception x) { 173 throw new Error(x); 174 } 175 } 176 177 @Override 178 public void send(DatagramPacket p) throws IOException { 179 ByteBuffer bb = null; 180 try { 181 InetSocketAddress target; 182 synchronized (p) { 183 // copy bytes to temporary direct buffer 184 int len = p.getLength(); 185 bb = Util.getTemporaryDirectBuffer(len); 186 bb.put(p.getData(), p.getOffset(), len); 187 bb.flip(); 188 189 // target address 190 if (p.getAddress() == null) { 191 InetSocketAddress remote = dc.remoteAddress(); 192 if (remote == null) { 193 // not specified by DatagramSocket 194 throw new IllegalArgumentException("Address not set"); 195 } 196 // set address/port to maintain compatibility with DatagramSocket 197 p.setAddress(remote.getAddress()); 198 p.setPort(remote.getPort()); 199 target = remote; 200 } else { 201 // throws IllegalArgumentException if port not set 202 target = (InetSocketAddress) p.getSocketAddress(); 203 } 204 } 205 // send datagram 206 try { 207 dc.blockingSend(bb, target); 208 } catch (AlreadyConnectedException e) { 209 throw new IllegalArgumentException("Connected and packet address differ"); 210 } catch (ClosedChannelException e) { 211 var exc = new SocketException("Socket closed"); 212 exc.initCause(e); 213 throw exc; 214 } 215 } finally { 216 if (bb != null) { 217 Util.offerFirstTemporaryDirectBuffer(bb); 218 } 219 } 220 } 221 222 @Override 223 public void receive(DatagramPacket p) throws IOException { 224 // get temporary direct buffer with a capacity of p.bufLength 225 int bufLength = DatagramPackets.getBufLength(p); 226 ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength); 227 try { 228 long nanos = MILLISECONDS.toNanos(timeout); 229 SocketAddress sender = dc.blockingReceive(bb, nanos); 230 bb.flip(); 231 synchronized (p) { 232 // copy bytes to the DatagramPacket and set length 233 int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p)); 234 bb.get(p.getData(), p.getOffset(), len); 235 DatagramPackets.setLength(p, len); 236 237 // sender address 238 p.setSocketAddress(sender); 239 } 240 } catch (ClosedChannelException e) { 241 var exc = new SocketException("Socket closed"); 242 exc.initCause(e); 243 throw exc; 244 } finally { 245 Util.offerFirstTemporaryDirectBuffer(bb); 246 } 247 } 248 249 @Override 250 public InetAddress getLocalAddress() { 251 if (isClosed()) 252 return null; 253 InetSocketAddress local = dc.localAddress(); 254 if (local == null) 255 local = new InetSocketAddress(0); 256 InetAddress result = local.getAddress(); 257 SecurityManager sm = System.getSecurityManager(); 258 if (sm != null) { 259 try { 260 sm.checkConnect(result.getHostAddress(), -1); 261 } catch (SecurityException x) { 262 return new InetSocketAddress(0).getAddress(); 263 } 264 } 265 return result; 266 } 267 268 @Override 269 public int getLocalPort() { 270 if (isClosed()) 271 return -1; 272 InetSocketAddress local = dc.localAddress(); 273 if (local != null) { 274 return local.getPort(); 275 } 276 return 0; 277 } 278 279 @Override 280 public void setSoTimeout(int timeout) throws SocketException { 281 if (isClosed()) 282 throw new SocketException("Socket is closed"); 283 if (timeout < 0) 284 throw new IllegalArgumentException("timeout < 0"); 285 this.timeout = timeout; 286 } 287 288 @Override 289 public int getSoTimeout() throws SocketException { 290 if (isClosed()) 291 throw new SocketException("Socket is closed"); 292 return timeout; 293 } 294 295 private void setBooleanOption(SocketOption<Boolean> name, boolean value) 296 throws SocketException 297 { 298 try { 299 dc.setOption(name, value); 300 } catch (IOException x) { 301 Net.translateToSocketException(x); 302 } 303 } 304 305 private void setIntOption(SocketOption<Integer> name, int value) 306 throws SocketException 307 { 308 try { 309 dc.setOption(name, value); 310 } catch (IOException x) { 311 Net.translateToSocketException(x); 312 } 313 } 314 315 private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException { 316 try { 317 return dc.getOption(name).booleanValue(); 318 } catch (IOException x) { 319 Net.translateToSocketException(x); 320 return false; // keep compiler happy 321 } 322 } 323 324 private int getIntOption(SocketOption<Integer> name) throws SocketException { 325 try { 326 return dc.getOption(name).intValue(); 327 } catch (IOException x) { 328 Net.translateToSocketException(x); 329 return -1; // keep compiler happy 330 } 331 } 332 333 @Override 334 public void setSendBufferSize(int size) throws SocketException { 335 if (size <= 0) 336 throw new IllegalArgumentException("Invalid send size"); 337 setIntOption(StandardSocketOptions.SO_SNDBUF, size); 338 } 339 340 @Override 341 public int getSendBufferSize() throws SocketException { 342 return getIntOption(StandardSocketOptions.SO_SNDBUF); 343 } 344 345 @Override 346 public void setReceiveBufferSize(int size) throws SocketException { 347 if (size <= 0) 348 throw new IllegalArgumentException("Invalid receive size"); 349 setIntOption(StandardSocketOptions.SO_RCVBUF, size); 350 } 351 352 @Override 353 public int getReceiveBufferSize() throws SocketException { 354 return getIntOption(StandardSocketOptions.SO_RCVBUF); 355 } 356 357 @Override 358 public void setReuseAddress(boolean on) throws SocketException { 359 setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); 360 } 361 362 @Override 363 public boolean getReuseAddress() throws SocketException { 364 return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); 365 } 366 367 @Override 368 public void setBroadcast(boolean on) throws SocketException { 369 setBooleanOption(StandardSocketOptions.SO_BROADCAST, on); 370 } 371 372 @Override 373 public boolean getBroadcast() throws SocketException { 374 return getBooleanOption(StandardSocketOptions.SO_BROADCAST); 375 } 376 377 @Override 378 public void setTrafficClass(int tc) throws SocketException { 379 setIntOption(StandardSocketOptions.IP_TOS, tc); 380 } 381 382 @Override 383 public int getTrafficClass() throws SocketException { 384 return getIntOption(StandardSocketOptions.IP_TOS); 385 } 386 387 @Override 388 public void close() { 389 try { 390 dc.close(); 391 } catch (IOException x) { 392 throw new Error(x); 393 } 394 } 395 396 @Override 397 public boolean isClosed() { 398 return !dc.isOpen(); 399 } 400 401 @Override 402 public DatagramChannel getChannel() { 403 return dc; 404 } 405 406 @Override 407 public <T> DatagramSocket setOption(SocketOption<T> name, T value) throws IOException { 408 dc.setOption(name, value); 409 return this; 410 } 411 412 @Override 413 public <T> T getOption(SocketOption<T> name) throws IOException { 414 return dc.getOption(name); 415 } 416 417 @Override 418 public Set<SocketOption<?>> supportedOptions() { 419 return dc.supportedOptions(); 420 } 421 422 // -- java.net.MulticastSocket -- 423 424 // used to coordinate changing TTL with the deprecated send method 425 private final ReentrantLock sendLock = new ReentrantLock(); 426 427 // cached outgoing interface (for use by setInterface/getInterface) 428 private final Object outgoingInterfaceLock = new Object(); 429 private NetworkInterface outgoingNetworkInterface; 430 private InetAddress outgoingInetAddress; 431 432 @Override 433 @Deprecated 434 public void setTTL(byte ttl) throws IOException { 435 setTimeToLive(Byte.toUnsignedInt(ttl)); 436 } 437 438 @Override 439 public void setTimeToLive(int ttl) throws IOException { 440 sendLock.lock(); 441 try { 442 setIntOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl); 443 } finally { 444 sendLock.unlock(); 445 } 446 } 447 448 @Override 449 @Deprecated 450 public byte getTTL() throws IOException { 451 return (byte) getTimeToLive(); 452 } 453 454 @Override 455 public int getTimeToLive() throws IOException { 456 sendLock.lock(); 457 try { 458 return getIntOption(StandardSocketOptions.IP_MULTICAST_TTL); 459 } finally { 460 sendLock.unlock(); 461 } 462 } 463 464 @Override 465 @Deprecated 466 public void joinGroup(InetAddress group) throws IOException { 467 Objects.requireNonNull(group); 468 try { 469 joinGroup(new InetSocketAddress(group, 0), null); 470 } catch (IllegalArgumentException iae) { 471 // 1-arg joinGroup does not specify IllegalArgumentException 472 throw (SocketException) new SocketException().initCause(iae); 473 } 474 } 475 476 @Override 477 @Deprecated 478 public void leaveGroup(InetAddress group) throws IOException { 479 Objects.requireNonNull(group); 480 try { 481 leaveGroup(new InetSocketAddress(group, 0), null); 482 } catch (IllegalArgumentException iae) { 483 // 1-arg leaveGroup does not specify IllegalArgumentException 484 throw (SocketException) new SocketException().initCause(iae); 485 } 486 } 487 488 /** 489 * Checks a SocketAddress to ensure that it is a multicast address. 490 * 491 * @return the multicast group 492 * @throws IllegalArgumentException if group is null, an unsupported address 493 * type, or an unresolved address 494 * @throws SocketException if group is not a multicast address 495 */ 496 private static InetAddress checkGroup(SocketAddress mcastaddr) throws SocketException { 497 if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress)) 498 throw new IllegalArgumentException("Unsupported address type"); 499 InetAddress group = ((InetSocketAddress) mcastaddr).getAddress(); 500 if (group == null) 501 throw new IllegalArgumentException("Unresolved address"); 502 if (!group.isMulticastAddress()) 503 throw new SocketException("Not a multicast address"); 504 return group; 505 } 506 507 @Override 508 public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException { 509 InetAddress group = checkGroup(mcastaddr); 510 NetworkInterface ni = (netIf != null) ? netIf : defaultNetworkInterface(); 511 if (isClosed()) 512 throw new SocketException("Socket is closed"); 513 synchronized (this) { 514 MembershipKey key = dc.findMembership(group, ni); 515 if (key != null) { 516 // already a member but need to check permission anyway 517 SecurityManager sm = System.getSecurityManager(); 518 if (sm != null) 519 sm.checkMulticast(group); 520 throw new SocketException("Already a member of group"); 521 } 522 dc.join(group, ni); // checks permission 523 } 524 } 525 526 @Override 527 public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException { 528 InetAddress group = checkGroup(mcastaddr); 529 NetworkInterface ni = (netIf != null) ? netIf : defaultNetworkInterface(); 530 if (isClosed()) 531 throw new SocketException("Socket is closed"); 532 SecurityManager sm = System.getSecurityManager(); 533 if (sm != null) 534 sm.checkMulticast(group); 535 synchronized (this) { 536 MembershipKey key = dc.findMembership(group, ni); 537 if (key == null) 538 throw new SocketException("Not a member of group"); 539 key.drop(); 540 } 541 } 542 543 @Override 544 @Deprecated 545 public void setInterface(InetAddress inf) throws SocketException { 546 if (inf == null) 547 throw new SocketException("Invalid value 'null'"); 548 NetworkInterface ni = NetworkInterface.getByInetAddress(inf); 549 if (ni == null) { 550 String address = inf.getHostAddress(); 551 throw new SocketException("No network interface with address " + address); 552 } 553 synchronized (outgoingInterfaceLock) { 554 // set interface and update cached values 555 setNetworkInterface(ni); 556 outgoingNetworkInterface = ni; 557 outgoingInetAddress = inf; 558 } 559 } 560 561 @Override 562 @Deprecated 563 public InetAddress getInterface() throws SocketException { 564 synchronized (outgoingInterfaceLock) { 565 NetworkInterface ni = outgoingNetworkInterface(); 566 if (ni != null) { 567 if (ni.equals(outgoingNetworkInterface)) { 568 return outgoingInetAddress; 569 } else { 570 // network interface has changed so update cached values 571 PrivilegedAction<InetAddress> pa; 572 pa = () -> ni.inetAddresses().findFirst().orElse(null); 573 InetAddress ia = AccessController.doPrivileged(pa); 574 if (ia == null) 575 throw new SocketException("Network interface has no IP address"); 576 outgoingNetworkInterface = ni; 577 outgoingInetAddress = ia; 578 return ia; 579 } 580 } 581 } 582 583 // no interface set 584 return anyInetAddress(); 585 } 586 587 @Override 588 public void setNetworkInterface(NetworkInterface netIf) throws SocketException { 589 try { 590 setOption(StandardSocketOptions.IP_MULTICAST_IF, netIf); 591 } catch (IOException e) { 592 Net.translateToSocketException(e); 593 } 594 } 595 596 @Override 597 public NetworkInterface getNetworkInterface() throws SocketException { 598 NetworkInterface ni = outgoingNetworkInterface(); 599 if (ni == null) { 600 // return NetworkInterface with index == 0 as placeholder 601 ni = anyNetworkInterface(); 602 } 603 return ni; 604 } 605 606 @Override 607 @Deprecated 608 public void setLoopbackMode(boolean disable) throws SocketException { 609 boolean enable = !disable; 610 setBooleanOption(StandardSocketOptions.IP_MULTICAST_LOOP, enable); 611 } 612 613 @Override 614 @Deprecated 615 public boolean getLoopbackMode() throws SocketException { 616 boolean enabled = getBooleanOption(StandardSocketOptions.IP_MULTICAST_LOOP); 617 return !enabled; 618 } 619 620 @Override 621 @Deprecated 622 public void send(DatagramPacket p, byte ttl) throws IOException { 623 sendLock.lock(); 624 try { 625 int oldValue = getTimeToLive(); 626 try { 627 setTTL(ttl); 628 send(p); 629 } finally { 630 setTimeToLive(oldValue); 631 } 632 } finally { 633 sendLock.unlock(); 634 } 635 } 636 637 /** 638 * Returns the outgoing NetworkInterface or null if not set. 639 */ 640 private NetworkInterface outgoingNetworkInterface() throws SocketException { 641 try { 642 return getOption(StandardSocketOptions.IP_MULTICAST_IF); 643 } catch (IOException e) { 644 Net.translateToSocketException(e); 645 return null; // keep compiler happy 646 } 647 } 648 649 /** 650 * Returns the default NetworkInterface to use when joining or leaving a 651 * multicast group and a network interface is not specified. 652 * This method will return the outgoing NetworkInterface if set, otherwise 653 * the result of NetworkInterface.getDefault(), otherwise a NetworkInterface 654 * with index == 0 as a placeholder for "any network interface". 655 */ 656 private NetworkInterface defaultNetworkInterface() throws SocketException { 657 NetworkInterface ni = outgoingNetworkInterface(); 658 if (ni == null) 659 ni = NetworkInterfaces.getDefault(); // macOS 660 if (ni == null) 661 ni = anyNetworkInterface(); 662 return ni; 663 } 664 665 /** 666 * Returns the placeholder for "any network interface", its index is 0. 667 */ 668 private NetworkInterface anyNetworkInterface() { 669 InetAddress[] addrs = new InetAddress[1]; 670 addrs[0] = anyInetAddress(); 671 return NetworkInterfaces.newNetworkInterface(addrs[0].getHostName(), 0, addrs); 672 } 673 674 /** 675 * Returns the InetAddress representing anyLocalAddress. 676 */ 677 private InetAddress anyInetAddress() { 678 return new InetSocketAddress(0).getAddress(); 679 } 680 681 /** 682 * Defines static methods to get/set DatagramPacket fields and workaround 683 * DatagramPacket deficiencies. 684 */ 685 private static class DatagramPackets { 686 private static final VarHandle LENGTH; 687 private static final VarHandle BUF_LENGTH; 688 static { 689 try { 690 PrivilegedExceptionAction<Lookup> pa = () -> 691 MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup()); 692 MethodHandles.Lookup l = AccessController.doPrivileged(pa); 693 LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class); 694 BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class); 695 } catch (Exception e) { 696 throw new ExceptionInInitializerError(e); 697 } 698 } 699 700 /** 701 * Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be 702 * used at this time because it sets both the length and bufLength fields. 703 */ 704 static void setLength(DatagramPacket p, int value) { 705 synchronized (p) { 706 LENGTH.set(p, value); 707 } 708 } 709 710 /** 711 * Returns the value of the DatagramPacket.bufLength field. 712 */ 713 static int getBufLength(DatagramPacket p) { 714 synchronized (p) { 715 return (int) BUF_LENGTH.get(p); 716 } 717 } 718 } 719 720 /** 721 * Defines static methods to invoke non-public NetworkInterface methods. 722 */ 723 private static class NetworkInterfaces { 724 static final MethodHandle GET_DEFAULT; 725 static final MethodHandle CONSTRUCTOR; 726 static { 727 try { 728 PrivilegedExceptionAction<Lookup> pa = () -> 729 MethodHandles.privateLookupIn(NetworkInterface.class, MethodHandles.lookup()); 730 MethodHandles.Lookup l = AccessController.doPrivileged(pa); 731 MethodType methodType = MethodType.methodType(NetworkInterface.class); 732 GET_DEFAULT = l.findStatic(NetworkInterface.class, "getDefault", methodType); 733 methodType = MethodType.methodType(void.class, String.class, int.class, InetAddress[].class); 734 CONSTRUCTOR = l.findConstructor(NetworkInterface.class, methodType); 735 } catch (Exception e) { 736 throw new ExceptionInInitializerError(e); 737 } 738 } 739 740 /** 741 * Returns the default network interface or null. 742 */ 743 static NetworkInterface getDefault() { 744 try { 745 return (NetworkInterface) GET_DEFAULT.invokeExact(); 746 } catch (Throwable e) { 747 throw new InternalError(e); 748 } 749 } 750 751 /** 752 * Creates a NetworkInterface with the given name index and addresses. 753 */ 754 static NetworkInterface newNetworkInterface(String name, int index, InetAddress[] addrs) { 755 try { 756 return (NetworkInterface) CONSTRUCTOR.invoke(name, index, addrs); 757 } catch (Throwable e) { 758 throw new InternalError(e); 759 } 760 } 761 } 762 }