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