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