1 /*
   2  * Copyright (c) 2001, 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 
  26 package sun.nio.ch;
  27 
  28 import java.io.IOException;
  29 import java.io.UncheckedIOException;
  30 import java.lang.invoke.MethodHandles;
  31 import java.lang.invoke.MethodHandles.Lookup;
  32 import java.lang.invoke.VarHandle;
  33 import java.net.DatagramPacket;
  34 import java.net.DatagramSocket;
  35 import java.net.DatagramSocketImpl;
  36 import java.net.InetAddress;
  37 import java.net.InetSocketAddress;
  38 import java.net.NetworkInterface;
  39 import java.net.SocketAddress;
  40 import java.net.SocketException;
  41 import java.net.SocketOption;
  42 import java.net.StandardSocketOptions;
  43 import java.nio.ByteBuffer;
  44 import java.nio.channels.AlreadyConnectedException;
  45 import java.nio.channels.ClosedChannelException;
  46 import java.nio.channels.DatagramChannel;
  47 import java.security.AccessController;
  48 import java.security.PrivilegedAction;
  49 import java.util.Set;
  50 
  51 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  52 
  53 // Make a datagram-socket channel look like a datagram socket.
  54 //
  55 // The methods in this class are defined in exactly the same order as in
  56 // java.net.DatagramSocket so as to simplify tracking future changes to that
  57 // class.
  58 //
  59 
  60 class DatagramSocketAdaptor
  61     extends DatagramSocket
  62 {
  63     // The channel being adapted
  64     private final DatagramChannelImpl dc;
  65 
  66     // Timeout "option" value for receives
  67     private volatile int timeout;
  68 
  69     // create DatagramSocket with useless impl
  70     private DatagramSocketAdaptor(DatagramChannelImpl dc) {
  71         super(new DummyDatagramSocketImpl());
  72         this.dc = dc;
  73     }
  74 
  75     static DatagramSocket create(DatagramChannelImpl dc) {
  76         return new DatagramSocketAdaptor(dc);
  77     }
  78 
  79     private void connectInternal(SocketAddress remote) throws SocketException {
  80         try {
  81             dc.connect(remote, false); // skips check for already connected
  82         } catch (ClosedChannelException e) {
  83             // ignore
  84         } catch (Exception x) {
  85             Net.translateToSocketException(x);
  86         }
  87     }
  88 
  89     @Override
  90     public void bind(SocketAddress local) throws SocketException {
  91         if (local != null) {
  92             local = Net.asInetSocketAddress(local);
  93         } else {
  94             local = new InetSocketAddress(0);
  95         }
  96         try {
  97             dc.bind(local);
  98         } catch (Exception x) {
  99             Net.translateToSocketException(x);
 100         }
 101     }
 102 
 103     @Override
 104     public void connect(InetAddress address, int port) {
 105         if (address == null)
 106             throw new IllegalArgumentException("Address can't be null");
 107         try {
 108             connectInternal(new InetSocketAddress(address, port));
 109         } catch (SocketException x) {
 110             throw new UncheckedIOException(x);
 111         }
 112     }
 113 
 114     @Override
 115     public void connect(SocketAddress remote) throws SocketException {
 116         if (remote == null)
 117             throw new IllegalArgumentException("Address can't be null");
 118         connectInternal(Net.asInetSocketAddress(remote));
 119     }
 120 
 121     @Override
 122     public void disconnect() {
 123         try {
 124             dc.disconnect();
 125         } catch (IOException x) {
 126             throw new UncheckedIOException(x);
 127         }
 128     }
 129 
 130     @Override
 131     public boolean isBound() {
 132         return dc.localAddress() != null;
 133     }
 134 
 135     @Override
 136     public boolean isConnected() {
 137         return dc.remoteAddress() != null;
 138     }
 139 
 140     @Override
 141     public InetAddress getInetAddress() {
 142         InetSocketAddress remote = dc.remoteAddress();
 143         return (remote != null) ? remote.getAddress() : null;
 144     }
 145 
 146     @Override
 147     public int getPort() {
 148         InetSocketAddress remote = dc.remoteAddress();
 149         return (remote != null) ? remote.getPort() : -1;
 150     }
 151 
 152     @Override
 153     public SocketAddress getRemoteSocketAddress() {
 154         return dc.remoteAddress();
 155     }
 156 
 157     @Override
 158     public SocketAddress getLocalSocketAddress() {
 159         try {
 160             return dc.getLocalAddress();
 161         } catch (ClosedChannelException e) {
 162             return null;
 163         } catch (Exception x) {
 164             throw new Error(x);
 165         }
 166     }
 167 
 168     @Override
 169     public void send(DatagramPacket p) throws IOException {
 170         ByteBuffer bb = null;
 171         try {
 172             InetSocketAddress target;
 173             synchronized (p) {
 174                 // copy bytes to temporary direct buffer
 175                 int len = p.getLength();
 176                 bb = Util.getTemporaryDirectBuffer(len);
 177                 bb.put(p.getData(), p.getOffset(), len);
 178                 bb.flip();
 179 
 180                 // target address
 181                 if (p.getAddress() == null) {
 182                     InetSocketAddress remote = dc.remoteAddress();
 183                     if (remote == null) {
 184                         // not specified by DatagramSocket
 185                         throw new IllegalArgumentException("Address not set");
 186                     }
 187                     // set address/port to maintain compatibility with DatagramSocket
 188                     p.setAddress(remote.getAddress());
 189                     p.setPort(remote.getPort());
 190                     target = remote;
 191                 } else {
 192                     // throws IllegalArgumentException if port not set
 193                     target = (InetSocketAddress) p.getSocketAddress();
 194                 }
 195             }
 196             // send datagram
 197             try {
 198                 dc.blockingSend(bb, target);
 199             } catch (AlreadyConnectedException e) {
 200                 throw new IllegalArgumentException("Connected and packet address differ");
 201             } catch (ClosedChannelException e) {
 202                 var exc = new SocketException("Socket closed");
 203                 exc.initCause(e);
 204                 throw exc;
 205             }
 206         } finally {
 207             if (bb != null) {
 208                 Util.offerFirstTemporaryDirectBuffer(bb);
 209             }
 210         }
 211     }
 212 
 213     @Override
 214     public void receive(DatagramPacket p) throws IOException {
 215         // get temporary direct buffer with a capacity of p.bufLength
 216         int bufLength = DatagramPackets.getBufLength(p);
 217         ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength);
 218         try {
 219             long nanos = MILLISECONDS.toNanos(timeout);
 220             SocketAddress sender = dc.blockingReceive(bb, nanos);
 221             bb.flip();
 222             synchronized (p) {
 223                 // copy bytes to the DatagramPacket and set length
 224                 int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p));
 225                 bb.get(p.getData(), p.getOffset(), len);
 226                 DatagramPackets.setLength(p, len);
 227 
 228                 // sender address
 229                 p.setSocketAddress(sender);
 230             }
 231         } catch (ClosedChannelException e) {
 232             var exc = new SocketException("Socket closed");
 233             exc.initCause(e);
 234             throw exc;
 235         } finally {
 236             Util.offerFirstTemporaryDirectBuffer(bb);
 237         }
 238     }
 239 
 240     @Override
 241     public InetAddress getLocalAddress() {
 242         if (isClosed())
 243             return null;
 244         InetSocketAddress local = dc.localAddress();
 245         if (local == null)
 246             local = new InetSocketAddress(0);
 247         InetAddress result = local.getAddress();
 248         SecurityManager sm = System.getSecurityManager();
 249         if (sm != null) {
 250             try {
 251                 sm.checkConnect(result.getHostAddress(), -1);
 252             } catch (SecurityException x) {
 253                 return new InetSocketAddress(0).getAddress();
 254             }
 255         }
 256         return result;
 257     }
 258 
 259     @Override
 260     public int getLocalPort() {
 261         if (isClosed())
 262             return -1;
 263         InetSocketAddress local = dc.localAddress();
 264         if (local != null) {
 265             return local.getPort();
 266         }
 267         return 0;
 268     }
 269 
 270     @Override
 271     public void setSoTimeout(int timeout) throws SocketException {
 272         if (isClosed())
 273             throw new SocketException("Socket is closed");
 274         if (timeout < 0)
 275             throw new IllegalArgumentException("timeout < 0");
 276         this.timeout = timeout;
 277     }
 278 
 279     @Override
 280     public int getSoTimeout() throws SocketException {
 281         if (isClosed())
 282             throw new SocketException("Socket is closed");
 283         return timeout;
 284     }
 285 
 286     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
 287         throws SocketException
 288     {
 289         try {
 290             dc.setOption(name, value);
 291         } catch (IOException x) {
 292             Net.translateToSocketException(x);
 293         }
 294     }
 295 
 296     private void setIntOption(SocketOption<Integer> name, int value)
 297         throws SocketException
 298     {
 299         try {
 300             dc.setOption(name, value);
 301         } catch (IOException x) {
 302             Net.translateToSocketException(x);
 303         }
 304     }
 305 
 306     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
 307         try {
 308             return dc.getOption(name).booleanValue();
 309         } catch (IOException x) {
 310             Net.translateToSocketException(x);
 311             return false;       // keep compiler happy
 312         }
 313     }
 314 
 315     private int getIntOption(SocketOption<Integer> name) throws SocketException {
 316         try {
 317             return dc.getOption(name).intValue();
 318         } catch (IOException x) {
 319             Net.translateToSocketException(x);
 320             return -1;          // keep compiler happy
 321         }
 322     }
 323 
 324     @Override
 325     public void setSendBufferSize(int size) throws SocketException {
 326         if (size <= 0)
 327             throw new IllegalArgumentException("Invalid send size");
 328         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
 329     }
 330 
 331     @Override
 332     public int getSendBufferSize() throws SocketException {
 333         return getIntOption(StandardSocketOptions.SO_SNDBUF);
 334     }
 335 
 336     @Override
 337     public void setReceiveBufferSize(int size) throws SocketException {
 338         if (size <= 0)
 339             throw new IllegalArgumentException("Invalid receive size");
 340         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
 341     }
 342 
 343     @Override
 344     public int getReceiveBufferSize() throws SocketException {
 345         return getIntOption(StandardSocketOptions.SO_RCVBUF);
 346     }
 347 
 348     @Override
 349     public void setReuseAddress(boolean on) throws SocketException {
 350         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
 351     }
 352 
 353     @Override
 354     public boolean getReuseAddress() throws SocketException {
 355         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
 356     }
 357 
 358     @Override
 359     public void setBroadcast(boolean on) throws SocketException {
 360         setBooleanOption(StandardSocketOptions.SO_BROADCAST, on);
 361     }
 362 
 363     @Override
 364     public boolean getBroadcast() throws SocketException {
 365         return getBooleanOption(StandardSocketOptions.SO_BROADCAST);
 366     }
 367 
 368     @Override
 369     public void setTrafficClass(int tc) throws SocketException {
 370         setIntOption(StandardSocketOptions.IP_TOS, tc);
 371     }
 372 
 373     @Override
 374     public int getTrafficClass() throws SocketException {
 375         return getIntOption(StandardSocketOptions.IP_TOS);
 376     }
 377 
 378     @Override
 379     public void close() {
 380         try {
 381             dc.close();
 382         } catch (IOException x) {
 383             throw new Error(x);
 384         }
 385     }
 386 
 387     @Override
 388     public boolean isClosed() {
 389         return !dc.isOpen();
 390     }
 391 
 392     @Override
 393     public DatagramChannel getChannel() {
 394         return dc;
 395     }
 396 
 397     @Override
 398     public <T> DatagramSocket setOption(SocketOption<T> name, T value) throws IOException {
 399         dc.setOption(name, value);
 400         return this;
 401     }
 402 
 403     @Override
 404     public <T> T getOption(SocketOption<T> name) throws IOException {
 405         return dc.getOption(name);
 406     }
 407 
 408     @Override
 409     public Set<SocketOption<?>> supportedOptions() {
 410         return dc.supportedOptions();
 411     }
 412 
 413 
 414     /**
 415      * DatagramSocketImpl implementation where all methods throw an error.
 416      */
 417     private static class DummyDatagramSocketImpl extends DatagramSocketImpl {
 418         private static <T> T shouldNotGetHere() {
 419             throw new InternalError("Should not get here");
 420         }
 421 
 422         @Override
 423         protected void create() {
 424             shouldNotGetHere();
 425         }
 426 
 427         @Override
 428         protected void bind(int lport, InetAddress laddr) {
 429             shouldNotGetHere();
 430         }
 431 
 432         @Override
 433         protected void send(DatagramPacket p) {
 434             shouldNotGetHere();
 435         }
 436 
 437         @Override
 438         protected int peek(InetAddress address) {
 439             return shouldNotGetHere();
 440         }
 441 
 442         @Override
 443         protected int peekData(DatagramPacket p) {
 444             return shouldNotGetHere();
 445         }
 446 
 447         @Override
 448         protected void receive(DatagramPacket p) {
 449             shouldNotGetHere();
 450         }
 451 
 452         @Deprecated
 453         protected void setTTL(byte ttl) {
 454             shouldNotGetHere();
 455         }
 456 
 457         @Deprecated
 458         protected byte getTTL() {
 459             return shouldNotGetHere();
 460         }
 461 
 462         @Override
 463         protected void setTimeToLive(int ttl) {
 464             shouldNotGetHere();
 465         }
 466 
 467         @Override
 468         protected int getTimeToLive() {
 469             return shouldNotGetHere();
 470         }
 471 
 472         @Override
 473         protected void join(InetAddress group) {
 474             shouldNotGetHere();
 475         }
 476 
 477         @Override
 478         protected void leave(InetAddress inetaddr) {
 479             shouldNotGetHere();
 480         }
 481 
 482         @Override
 483         protected void joinGroup(SocketAddress group, NetworkInterface netIf) {
 484             shouldNotGetHere();
 485         }
 486 
 487         @Override
 488         protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
 489             shouldNotGetHere();
 490         }
 491 
 492         @Override
 493         protected void close() {
 494             shouldNotGetHere();
 495         }
 496 
 497         @Override
 498         public Object getOption(int optID) {
 499             return shouldNotGetHere();
 500         }
 501 
 502         @Override
 503         public void setOption(int optID, Object value) {
 504             shouldNotGetHere();
 505         }
 506 
 507         @Override
 508         protected <T> void setOption(SocketOption<T> name, T value) {
 509             shouldNotGetHere();
 510         }
 511 
 512         @Override
 513         protected <T> T getOption(SocketOption<T> name) {
 514             return shouldNotGetHere();
 515         }
 516 
 517         @Override
 518         protected Set<SocketOption<?>> supportedOptions() {
 519             return shouldNotGetHere();
 520         }
 521     }
 522 
 523     /**
 524      * Defines static methods to get/set DatagramPacket fields and workaround
 525      * DatagramPacket deficiencies.
 526      */
 527     private static class DatagramPackets {
 528         private static final VarHandle LENGTH;
 529         private static final VarHandle BUF_LENGTH;
 530         static {
 531             try {
 532                 PrivilegedAction<Lookup> pa = () -> {
 533                     try {
 534                         return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup());
 535                     } catch (Exception e) {
 536                         throw new ExceptionInInitializerError(e);
 537                     }
 538                 };
 539                 MethodHandles.Lookup l = AccessController.doPrivileged(pa);
 540                 LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class);
 541                 BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class);
 542             } catch (Exception e) {
 543                 throw new ExceptionInInitializerError(e);
 544             }
 545         }
 546 
 547         /**
 548          * Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be
 549          * used at this time because it sets both the length and bufLength fields.
 550          */
 551         static void setLength(DatagramPacket p, int value) {
 552             synchronized (p) {
 553                 LENGTH.set(p, value);
 554             }
 555         }
 556 
 557         /**
 558          * Returns the value of the DatagramPacket.bufLength field.
 559          */
 560         static int getBufLength(DatagramPacket p) {
 561             synchronized (p) {
 562                 return (int) BUF_LENGTH.get(p);
 563             }
 564         }
 565     }
 566 }