1 /*
   2  * Copyright (c) 2001, 2018, 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.net.DatagramPacket;
  30 import java.net.DatagramSocket;
  31 import java.net.DatagramSocketImpl;
  32 import java.net.InetAddress;
  33 import java.net.InetSocketAddress;
  34 import java.net.NetworkInterface;
  35 import java.net.SocketAddress;
  36 import java.net.SocketException;
  37 import java.net.SocketOption;
  38 import java.net.SocketTimeoutException;
  39 import java.net.StandardSocketOptions;
  40 import java.nio.ByteBuffer;
  41 import java.nio.channels.ClosedChannelException;
  42 import java.nio.channels.DatagramChannel;
  43 import java.nio.channels.IllegalBlockingModeException;
  44 
  45 
  46 // Make a datagram-socket channel look like a datagram socket.
  47 //
  48 // The methods in this class are defined in exactly the same order as in
  49 // java.net.DatagramSocket so as to simplify tracking future changes to that
  50 // class.
  51 //
  52 
  53 public class DatagramSocketAdaptor
  54     extends DatagramSocket
  55 {
  56 
  57     // The channel being adapted
  58     private final DatagramChannelImpl dc;
  59 
  60     // Timeout "option" value for receives
  61     private volatile int timeout;
  62 
  63     // ## super will create a useless impl
  64     private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
  65         // Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
  66         // passing a dummy DatagramSocketImpl object to aovid any native
  67         // resource allocation in super class and invoking our bind method
  68         // before the dc field is initialized.
  69         super(dummyDatagramSocket);
  70         this.dc = dc;
  71     }
  72 
  73     public static DatagramSocket create(DatagramChannelImpl dc) {
  74         try {
  75             return new DatagramSocketAdaptor(dc);
  76         } catch (IOException x) {
  77             throw new Error(x);
  78         }
  79     }
  80 
  81     private void connectInternal(SocketAddress remote)
  82         throws SocketException
  83     {
  84         InetSocketAddress isa = Net.asInetSocketAddress(remote);
  85         int port = isa.getPort();
  86         if (port < 0 || port > 0xFFFF)
  87             throw new IllegalArgumentException("connect: " + port);
  88         if (remote == null)
  89             throw new IllegalArgumentException("connect: null address");
  90         if (isClosed())
  91             return;
  92         try {
  93             dc.connect(remote);
  94         } catch (Exception x) {
  95             Net.translateToSocketException(x);
  96         }
  97     }
  98 
  99     public void bind(SocketAddress local) throws SocketException {
 100         try {
 101             if (local == null)
 102                 local = new InetSocketAddress(0);
 103             dc.bind(local);
 104         } catch (Exception x) {
 105             Net.translateToSocketException(x);
 106         }
 107     }
 108 
 109     public void connect(InetAddress address, int port) {
 110         try {
 111             connectInternal(new InetSocketAddress(address, port));
 112         } catch (SocketException x) {
 113             // Yes, j.n.DatagramSocket really does this
 114         }
 115     }
 116 
 117     public void connect(SocketAddress remote) throws SocketException {
 118         if (remote == null)
 119             throw new IllegalArgumentException("Address can't be null");
 120         connectInternal(remote);
 121     }
 122 
 123     public void disconnect() {
 124         try {
 125             dc.disconnect();
 126         } catch (IOException x) {
 127             throw new Error(x);
 128         }
 129     }
 130 
 131     public boolean isBound() {
 132         return dc.localAddress() != null;
 133     }
 134 
 135     public boolean isConnected() {
 136         return dc.remoteAddress() != null;
 137     }
 138 
 139     public InetAddress getInetAddress() {
 140         return (isConnected()
 141                 ? Net.asInetSocketAddress(dc.remoteAddress()).getAddress()
 142                 : null);
 143     }
 144 
 145     public int getPort() {
 146         return (isConnected()
 147                 ? Net.asInetSocketAddress(dc.remoteAddress()).getPort()
 148                 : -1);
 149     }
 150 
 151     public void send(DatagramPacket p) throws IOException {
 152         synchronized (dc.blockingLock()) {
 153             if (!dc.isBlocking())
 154                 throw new IllegalBlockingModeException();
 155             try {
 156                 synchronized (p) {
 157                     ByteBuffer bb = ByteBuffer.wrap(p.getData(),
 158                                                     p.getOffset(),
 159                                                     p.getLength());
 160                     if (dc.isConnected()) {
 161                         if (p.getAddress() == null) {
 162                             // Legacy DatagramSocket will send in this case
 163                             // and set address and port of the packet
 164                             InetSocketAddress isa = (InetSocketAddress)
 165                                                     dc.remoteAddress();
 166                             p.setPort(isa.getPort());
 167                             p.setAddress(isa.getAddress());
 168                             dc.write(bb);
 169                         } else {
 170                             // Target address may not match connected address
 171                             dc.send(bb, p.getSocketAddress());
 172                         }
 173                     } else {
 174                         // Not connected so address must be valid or throw
 175                         dc.send(bb, p.getSocketAddress());
 176                     }
 177                 }
 178             } catch (IOException x) {
 179                 Net.translateException(x);
 180             }
 181         }
 182     }
 183 
 184     // Must hold dc.blockingLock()
 185     //
 186     private SocketAddress receive(ByteBuffer bb) throws IOException {
 187         if (timeout == 0) {
 188             return dc.receive(bb);
 189         }
 190 
 191         dc.configureBlocking(false);
 192         try {
 193             SocketAddress sender;
 194             if ((sender = dc.receive(bb)) != null)
 195                 return sender;
 196             long to = timeout;
 197             for (;;) {
 198                 if (!dc.isOpen())
 199                      throw new ClosedChannelException();
 200                 long st = System.currentTimeMillis();
 201                 int result = dc.poll(Net.POLLIN, to);
 202                 if (result > 0 && ((result & Net.POLLIN) != 0)) {
 203                     if ((sender = dc.receive(bb)) != null)
 204                         return sender;
 205                 }
 206                 to -= System.currentTimeMillis() - st;
 207                 if (to <= 0)
 208                     throw new SocketTimeoutException();
 209             }
 210         } finally {
 211             try {
 212                 dc.configureBlocking(true);
 213             } catch (ClosedChannelException e) { }
 214         }
 215     }
 216 
 217     public void receive(DatagramPacket p) throws IOException {
 218         synchronized (dc.blockingLock()) {
 219             if (!dc.isBlocking())
 220                 throw new IllegalBlockingModeException();
 221             try {
 222                 synchronized (p) {
 223                     ByteBuffer bb = ByteBuffer.wrap(p.getData(),
 224                                                     p.getOffset(),
 225                                                     p.getLength());
 226                     SocketAddress sender = receive(bb);
 227                     p.setSocketAddress(sender);
 228                     p.setLength(bb.position() - p.getOffset());
 229                 }
 230             } catch (IOException x) {
 231                 Net.translateException(x);
 232             }
 233         }
 234     }
 235 
 236     public InetAddress getLocalAddress() {
 237         if (isClosed())
 238             return null;
 239         SocketAddress local = dc.localAddress();
 240         if (local == null)
 241             local = new InetSocketAddress(0);
 242         InetAddress result = ((InetSocketAddress)local).getAddress();
 243         SecurityManager sm = System.getSecurityManager();
 244         if (sm != null) {
 245             try {
 246                 sm.checkConnect(result.getHostAddress(), -1);
 247             } catch (SecurityException x) {
 248                 return new InetSocketAddress(0).getAddress();
 249             }
 250         }
 251         return result;
 252     }
 253 
 254     public int getLocalPort() {
 255         if (isClosed())
 256             return -1;
 257         try {
 258             SocketAddress local = dc.getLocalAddress();
 259             if (local != null) {
 260                 return ((InetSocketAddress)local).getPort();
 261             }
 262         } catch (Exception x) {
 263         }
 264         return 0;
 265     }
 266 
 267     public void setSoTimeout(int timeout) throws SocketException {
 268         this.timeout = timeout;
 269     }
 270 
 271     public int getSoTimeout() throws SocketException {
 272         return timeout;
 273     }
 274 
 275     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
 276         throws SocketException
 277     {
 278         try {
 279             dc.setOption(name, value);
 280         } catch (IOException x) {
 281             Net.translateToSocketException(x);
 282         }
 283     }
 284 
 285     private void setIntOption(SocketOption<Integer> name, int 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 boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
 296         try {
 297             return dc.getOption(name).booleanValue();
 298         } catch (IOException x) {
 299             Net.translateToSocketException(x);
 300             return false;       // keep compiler happy
 301         }
 302     }
 303 
 304     private int getIntOption(SocketOption<Integer> name) throws SocketException {
 305         try {
 306             return dc.getOption(name).intValue();
 307         } catch (IOException x) {
 308             Net.translateToSocketException(x);
 309             return -1;          // keep compiler happy
 310         }
 311     }
 312 
 313     public void setSendBufferSize(int size) throws SocketException {
 314         if (size <= 0)
 315             throw new IllegalArgumentException("Invalid send size");
 316         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
 317     }
 318 
 319     public int getSendBufferSize() throws SocketException {
 320         return getIntOption(StandardSocketOptions.SO_SNDBUF);
 321     }
 322 
 323     public void setReceiveBufferSize(int size) throws SocketException {
 324         if (size <= 0)
 325             throw new IllegalArgumentException("Invalid receive size");
 326         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
 327     }
 328 
 329     public int getReceiveBufferSize() throws SocketException {
 330         return getIntOption(StandardSocketOptions.SO_RCVBUF);
 331     }
 332 
 333     public void setReuseAddress(boolean on) throws SocketException {
 334         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
 335     }
 336 
 337     public boolean getReuseAddress() throws SocketException {
 338         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
 339 
 340     }
 341 
 342     public void setBroadcast(boolean on) throws SocketException {
 343         setBooleanOption(StandardSocketOptions.SO_BROADCAST, on);
 344     }
 345 
 346     public boolean getBroadcast() throws SocketException {
 347         return getBooleanOption(StandardSocketOptions.SO_BROADCAST);
 348     }
 349 
 350     public void setTrafficClass(int tc) throws SocketException {
 351         setIntOption(StandardSocketOptions.IP_TOS, tc);
 352     }
 353 
 354     public int getTrafficClass() throws SocketException {
 355         return getIntOption(StandardSocketOptions.IP_TOS);
 356     }
 357 
 358     public void close() {
 359         try {
 360             dc.close();
 361         } catch (IOException x) {
 362             throw new Error(x);
 363         }
 364     }
 365 
 366     public boolean isClosed() {
 367         return !dc.isOpen();
 368     }
 369 
 370     public DatagramChannel getChannel() {
 371         return dc;
 372     }
 373 
 374    /*
 375     * A dummy implementation of DatagramSocketImpl that can be passed to the
 376     * DatagramSocket constructor so that no native resources are allocated in
 377     * super class.
 378     */
 379    private static final DatagramSocketImpl dummyDatagramSocket
 380        = new DatagramSocketImpl()
 381    {
 382        protected void create() throws SocketException {}
 383 
 384        protected void bind(int lport, InetAddress laddr) throws SocketException {}
 385 
 386        protected void send(DatagramPacket p) throws IOException {}
 387 
 388        protected int peek(InetAddress i) throws IOException { return 0; }
 389 
 390        protected int peekData(DatagramPacket p) throws IOException { return 0; }
 391 
 392        protected void receive(DatagramPacket p) throws IOException {}
 393 
 394        @Deprecated
 395        protected void setTTL(byte ttl) throws IOException {}
 396 
 397        @Deprecated
 398        protected byte getTTL() throws IOException { return 0; }
 399 
 400        protected void setTimeToLive(int ttl) throws IOException {}
 401 
 402        protected int getTimeToLive() throws IOException { return 0;}
 403 
 404        protected void join(InetAddress inetaddr) throws IOException {}
 405 
 406        protected void leave(InetAddress inetaddr) throws IOException {}
 407 
 408        protected void joinGroup(SocketAddress mcastaddr,
 409                                  NetworkInterface netIf) throws IOException {}
 410 
 411        protected void leaveGroup(SocketAddress mcastaddr,
 412                                  NetworkInterface netIf) throws IOException {}
 413 
 414        protected void close() {}
 415 
 416        public Object getOption(int optID) throws SocketException { return null;}
 417 
 418        public void setOption(int optID, Object value) throws SocketException {}
 419    };
 420 }