1 /*
   2  * Copyright (c) 2001, 2008, 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 = 0;
  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 aovid 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         // Implement timeout with a selector
 180         SelectionKey sk = null;
 181         Selector sel = null;
 182         dc.configureBlocking(false);
 183         try {
 184             int n;
 185             SocketAddress sender;
 186             if ((sender = dc.receive(bb)) != null)
 187                 return sender;
 188             sel = Util.getTemporarySelector(dc);
 189             sk = dc.register(sel, SelectionKey.OP_READ);
 190             long to = timeout;
 191             for (;;) {
 192                 if (!dc.isOpen())
 193                      throw new ClosedChannelException();
 194                 long st = System.currentTimeMillis();
 195                 int ns = sel.select(to);
 196                 if (ns > 0 && sk.isReadable()) {
 197                     if ((sender = dc.receive(bb)) != null)
 198                         return sender;
 199                 }
 200                 sel.selectedKeys().remove(sk);
 201                 to -= System.currentTimeMillis() - st;
 202                 if (to <= 0)
 203                     throw new SocketTimeoutException();
 204 
 205             }
 206         } finally {
 207             if (sk != null)
 208                 sk.cancel();
 209             if (dc.isOpen())
 210                 dc.configureBlocking(true);
 211             if (sel != null)
 212                 Util.releaseTemporarySelector(sel);
 213         }
 214     }
 215 
 216     public void receive(DatagramPacket p) throws IOException {
 217         synchronized (dc.blockingLock()) {
 218             if (!dc.isBlocking())
 219                 throw new IllegalBlockingModeException();
 220             try {
 221                 synchronized (p) {
 222                     ByteBuffer bb = ByteBuffer.wrap(p.getData(),
 223                                                     p.getOffset(),
 224                                                     p.getLength());
 225                     SocketAddress sender = receive(bb);
 226                     p.setSocketAddress(sender);
 227                     p.setLength(bb.position() - p.getOffset());
 228                 }
 229             } catch (IOException x) {
 230                 Net.translateException(x);
 231             }
 232         }
 233     }
 234 
 235     public InetAddress getLocalAddress() {
 236         if (isClosed())
 237             return null;
 238         SocketAddress local = dc.localAddress();
 239         if (local == null)
 240             local = new InetSocketAddress(0);
 241         InetAddress result = ((InetSocketAddress)local).getAddress();
 242         SecurityManager sm = System.getSecurityManager();
 243         if (sm != null) {
 244             try {
 245                 sm.checkConnect(result.getHostAddress(), -1);
 246             } catch (SecurityException x) {
 247                 return new InetSocketAddress(0).getAddress();
 248             }
 249         }
 250         return result;
 251     }
 252 
 253     public int getLocalPort() {
 254         if (isClosed())
 255             return -1;
 256         try {
 257             SocketAddress local = dc.getLocalAddress();
 258             if (local != null) {
 259                 return ((InetSocketAddress)local).getPort();
 260             }
 261         } catch (Exception x) {
 262         }
 263         return 0;
 264     }
 265 
 266     public void setSoTimeout(int timeout) throws SocketException {
 267         this.timeout = timeout;
 268     }
 269 
 270     public int getSoTimeout() throws SocketException {
 271         return timeout;
 272     }
 273 
 274     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
 275         throws SocketException
 276     {
 277         try {
 278             dc.setOption(name, value);
 279         } catch (IOException x) {
 280             Net.translateToSocketException(x);
 281         }
 282     }
 283 
 284     private void setIntOption(SocketOption<Integer> name, int value)
 285         throws SocketException
 286     {
 287         try {
 288             dc.setOption(name, value);
 289         } catch (IOException x) {
 290             Net.translateToSocketException(x);
 291         }
 292     }
 293 
 294     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
 295         try {
 296             return dc.getOption(name).booleanValue();
 297         } catch (IOException x) {
 298             Net.translateToSocketException(x);
 299             return false;       // keep compiler happy
 300         }
 301     }
 302 
 303     private int getIntOption(SocketOption<Integer> name) throws SocketException {
 304         try {
 305             return dc.getOption(name).intValue();
 306         } catch (IOException x) {
 307             Net.translateToSocketException(x);
 308             return -1;          // keep compiler happy
 309         }
 310     }
 311 
 312     public void setSendBufferSize(int size) throws SocketException {
 313         if (size <= 0)
 314             throw new IllegalArgumentException("Invalid send size");
 315         setIntOption(StandardSocketOption.SO_SNDBUF, size);
 316     }
 317 
 318     public int getSendBufferSize() throws SocketException {
 319         return getIntOption(StandardSocketOption.SO_SNDBUF);
 320     }
 321 
 322     public void setReceiveBufferSize(int size) throws SocketException {
 323         if (size <= 0)
 324             throw new IllegalArgumentException("Invalid receive size");
 325         setIntOption(StandardSocketOption.SO_RCVBUF, size);
 326     }
 327 
 328     public int getReceiveBufferSize() throws SocketException {
 329         return getIntOption(StandardSocketOption.SO_RCVBUF);
 330     }
 331 
 332     public void setReuseAddress(boolean on) throws SocketException {
 333         setBooleanOption(StandardSocketOption.SO_REUSEADDR, on);
 334     }
 335 
 336     public boolean getReuseAddress() throws SocketException {
 337         return getBooleanOption(StandardSocketOption.SO_REUSEADDR);
 338 
 339     }
 340 
 341     public void setBroadcast(boolean on) throws SocketException {
 342         setBooleanOption(StandardSocketOption.SO_BROADCAST, on);
 343     }
 344 
 345     public boolean getBroadcast() throws SocketException {
 346         return getBooleanOption(StandardSocketOption.SO_BROADCAST);
 347     }
 348 
 349     public void setTrafficClass(int tc) throws SocketException {
 350         setIntOption(StandardSocketOption.IP_TOS, tc);
 351     }
 352 
 353     public int getTrafficClass() throws SocketException {
 354         return getIntOption(StandardSocketOption.IP_TOS);
 355     }
 356 
 357     public void close() {
 358         try {
 359             dc.close();
 360         } catch (IOException x) {
 361             throw new Error(x);
 362         }
 363     }
 364 
 365     public boolean isClosed() {
 366         return !dc.isOpen();
 367     }
 368 
 369     public DatagramChannel getChannel() {
 370         return dc;
 371     }
 372 
 373    /*
 374     * A dummy implementation of DatagramSocketImpl that can be passed to the
 375     * DatagramSocket constructor so that no native resources are allocated in
 376     * super class.
 377     */
 378    private static final DatagramSocketImpl dummyDatagramSocket
 379        = new DatagramSocketImpl()
 380    {
 381        protected void create() throws SocketException {}
 382 
 383        protected void bind(int lport, InetAddress laddr) throws SocketException {}
 384 
 385        protected void send(DatagramPacket p) throws IOException {}
 386 
 387        protected int peek(InetAddress i) throws IOException { return 0; }
 388 
 389        protected int peekData(DatagramPacket p) throws IOException { return 0; }
 390 
 391        protected void receive(DatagramPacket p) throws IOException {}
 392 
 393        protected void setTTL(byte ttl) throws IOException {}
 394 
 395        protected byte getTTL() throws IOException { return 0; }
 396 
 397        protected void setTimeToLive(int ttl) throws IOException {}
 398 
 399        protected int getTimeToLive() throws IOException { return 0;}
 400 
 401        protected void join(InetAddress inetaddr) throws IOException {}
 402 
 403        protected void leave(InetAddress inetaddr) throws IOException {}
 404 
 405        protected void joinGroup(SocketAddress mcastaddr,
 406                                  NetworkInterface netIf) throws IOException {}
 407 
 408        protected void leaveGroup(SocketAddress mcastaddr,
 409                                  NetworkInterface netIf) throws IOException {}
 410 
 411        protected void close() {}
 412 
 413        public Object getOption(int optID) throws SocketException { return null;}
 414 
 415        public void setOption(int optID, Object value) throws SocketException {}
 416    };
 417 }