1 /*
   2  * Copyright (c) 2000, 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.io.InputStream;
  30 import java.io.OutputStream;
  31 import java.net.InetAddress;
  32 import java.net.InetSocketAddress;
  33 import java.net.Socket;
  34 import java.net.SocketAddress;
  35 import java.net.SocketException;
  36 import java.net.SocketImpl;
  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.Channels;
  42 import java.nio.channels.ClosedChannelException;
  43 import java.nio.channels.IllegalBlockingModeException;
  44 import java.nio.channels.SocketChannel;
  45 import java.security.AccessController;
  46 import java.security.PrivilegedExceptionAction;
  47 import java.util.concurrent.TimeUnit;
  48 
  49 // Make a socket channel look like a socket.
  50 //
  51 // The only aspects of java.net.Socket-hood that we don't attempt to emulate
  52 // here are the interrupted-I/O exceptions (which our Solaris implementations
  53 // attempt to support) and the sending of urgent data.  Otherwise an adapted
  54 // socket should look enough like a real java.net.Socket to fool most of the
  55 // developers most of the time, right down to the exception message strings.
  56 //
  57 // The methods in this class are defined in exactly the same order as in
  58 // java.net.Socket so as to simplify tracking future changes to that class.
  59 //
  60 
  61 class SocketAdaptor
  62     extends Socket
  63 {
  64 
  65     // The channel being adapted
  66     private final SocketChannelImpl sc;
  67 
  68     // Timeout "option" value for reads
  69     private volatile int timeout;
  70 
  71     private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
  72         super((SocketImpl) null);
  73         this.sc = sc;
  74     }
  75 
  76     public static Socket create(SocketChannelImpl sc) {
  77         try {
  78             return new SocketAdaptor(sc);
  79         } catch (SocketException e) {
  80             throw new InternalError("Should not reach here");
  81         }
  82     }
  83 
  84     public SocketChannel getChannel() {
  85         return sc;
  86     }
  87 
  88     // Override this method just to protect against changes in the superclass
  89     //
  90     public void connect(SocketAddress remote) throws IOException {
  91         connect(remote, 0);
  92     }
  93 
  94     public void connect(SocketAddress remote, int timeout) throws IOException {
  95         if (remote == null)
  96             throw new IllegalArgumentException("connect: The address can't be null");
  97         if (timeout < 0)
  98             throw new IllegalArgumentException("connect: timeout can't be negative");
  99 
 100         synchronized (sc.blockingLock()) {
 101             if (!sc.isBlocking())
 102                 throw new IllegalBlockingModeException();
 103 
 104             try {
 105                 if (timeout == 0) {
 106                     sc.connect(remote);
 107                     return;
 108                 }
 109 
 110                 sc.configureBlocking(false);
 111                 try {
 112                     if (sc.connect(remote))
 113                         return;
 114                     long timeoutNanos =
 115                         TimeUnit.NANOSECONDS.convert(timeout,
 116                             TimeUnit.MILLISECONDS);
 117                     for (;;) {
 118                         if (!sc.isOpen())
 119                             throw new ClosedChannelException();
 120                         long startTime = System.nanoTime();
 121 
 122                         int result = sc.poll(Net.POLLCONN, timeout);
 123                         if (result > 0 && sc.finishConnect())
 124                             break;
 125                         timeoutNanos -= System.nanoTime() - startTime;
 126                         if (timeoutNanos <= 0) {
 127                             try {
 128                                 sc.close();
 129                             } catch (IOException x) { }
 130                             throw new SocketTimeoutException();
 131                         }
 132                     }
 133                 } finally {
 134                     try {
 135                         sc.configureBlocking(true);
 136                     } catch (ClosedChannelException e) { }
 137                 }
 138 
 139             } catch (Exception x) {
 140                 Net.translateException(x, true);
 141             }
 142         }
 143 
 144     }
 145 
 146     public void bind(SocketAddress local) throws IOException {
 147         try {
 148             sc.bind(local);
 149         } catch (Exception x) {
 150             Net.translateException(x);
 151         }
 152     }
 153 
 154     public InetAddress getInetAddress() {
 155         SocketAddress remote = sc.remoteAddress();
 156         if (remote == null) {
 157             return null;
 158         } else {
 159             return ((InetSocketAddress)remote).getAddress();
 160         }
 161     }
 162 
 163     public InetAddress getLocalAddress() {
 164         if (sc.isOpen()) {
 165             InetSocketAddress local = sc.localAddress();
 166             if (local != null) {
 167                 return Net.getRevealedLocalAddress(local).getAddress();
 168             }
 169         }
 170         return new InetSocketAddress(0).getAddress();
 171     }
 172 
 173     public int getPort() {
 174         SocketAddress remote = sc.remoteAddress();
 175         if (remote == null) {
 176             return 0;
 177         } else {
 178             return ((InetSocketAddress)remote).getPort();
 179         }
 180     }
 181 
 182     public int getLocalPort() {
 183         SocketAddress local = sc.localAddress();
 184         if (local == null) {
 185             return -1;
 186         } else {
 187             return ((InetSocketAddress)local).getPort();
 188         }
 189     }
 190 
 191     private class SocketInputStream
 192         extends ChannelInputStream
 193     {
 194         private SocketInputStream() {
 195             super(sc);
 196         }
 197 
 198         protected int read(ByteBuffer bb)
 199             throws IOException
 200         {
 201             synchronized (sc.blockingLock()) {
 202                 if (!sc.isBlocking())
 203                     throw new IllegalBlockingModeException();
 204 
 205                 if (timeout == 0)
 206                     return sc.read(bb);
 207 
 208                 sc.configureBlocking(false);
 209                 try {
 210                     int n;
 211                     if ((n = sc.read(bb)) != 0)
 212                         return n;
 213                     long timeoutNanos =
 214                         TimeUnit.NANOSECONDS.convert(timeout,
 215                             TimeUnit.MILLISECONDS);
 216                     for (;;) {
 217                         if (!sc.isOpen())
 218                             throw new ClosedChannelException();
 219                         long startTime = System.nanoTime();
 220                         int result = sc.poll(Net.POLLIN, timeout);
 221                         if (result > 0) {
 222                             if ((n = sc.read(bb)) != 0)
 223                                 return n;
 224                         }
 225                         timeoutNanos -= System.nanoTime() - startTime;
 226                         if (timeoutNanos <= 0)
 227                             throw new SocketTimeoutException();
 228                     }
 229                 } finally {
 230                     try {
 231                         sc.configureBlocking(true);
 232                     } catch (ClosedChannelException e) { }
 233                 }
 234             }
 235         }
 236     }
 237 
 238     private InputStream socketInputStream = null;
 239 
 240     public InputStream getInputStream() throws IOException {
 241         if (!sc.isOpen())
 242             throw new SocketException("Socket is closed");
 243         if (!sc.isConnected())
 244             throw new SocketException("Socket is not connected");
 245         if (!sc.isInputOpen())
 246             throw new SocketException("Socket input is shutdown");
 247         if (socketInputStream == null) {
 248             try {
 249                 socketInputStream = AccessController.doPrivileged(
 250                     new PrivilegedExceptionAction<InputStream>() {
 251                         public InputStream run() throws IOException {
 252                             return new SocketInputStream();
 253                         }
 254                     });
 255             } catch (java.security.PrivilegedActionException e) {
 256                 throw (IOException)e.getException();
 257             }
 258         }
 259         return socketInputStream;
 260     }
 261 
 262     public OutputStream getOutputStream() throws IOException {
 263         if (!sc.isOpen())
 264             throw new SocketException("Socket is closed");
 265         if (!sc.isConnected())
 266             throw new SocketException("Socket is not connected");
 267         if (!sc.isOutputOpen())
 268             throw new SocketException("Socket output is shutdown");
 269         OutputStream os = null;
 270         try {
 271             os = AccessController.doPrivileged(
 272                 new PrivilegedExceptionAction<OutputStream>() {
 273                     public OutputStream run() throws IOException {
 274                         return Channels.newOutputStream(sc);
 275                     }
 276                 });
 277         } catch (java.security.PrivilegedActionException e) {
 278             throw (IOException)e.getException();
 279         }
 280         return os;
 281     }
 282 
 283     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
 284         throws SocketException
 285     {
 286         try {
 287             sc.setOption(name, value);
 288         } catch (IOException x) {
 289             Net.translateToSocketException(x);
 290         }
 291     }
 292 
 293     private void setIntOption(SocketOption<Integer> name, int value)
 294         throws SocketException
 295     {
 296         try {
 297             sc.setOption(name, value);
 298         } catch (IOException x) {
 299             Net.translateToSocketException(x);
 300         }
 301     }
 302 
 303     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
 304         try {
 305             return sc.getOption(name).booleanValue();
 306         } catch (IOException x) {
 307             Net.translateToSocketException(x);
 308             return false;       // keep compiler happy
 309         }
 310     }
 311 
 312     private int getIntOption(SocketOption<Integer> name) throws SocketException {
 313         try {
 314             return sc.getOption(name).intValue();
 315         } catch (IOException x) {
 316             Net.translateToSocketException(x);
 317             return -1;          // keep compiler happy
 318         }
 319     }
 320 
 321     public void setTcpNoDelay(boolean on) throws SocketException {
 322         setBooleanOption(StandardSocketOptions.TCP_NODELAY, on);
 323     }
 324 
 325     public boolean getTcpNoDelay() throws SocketException {
 326         return getBooleanOption(StandardSocketOptions.TCP_NODELAY);
 327     }
 328 
 329     public void setSoLinger(boolean on, int linger) throws SocketException {
 330         if (!on)
 331             linger = -1;
 332         setIntOption(StandardSocketOptions.SO_LINGER, linger);
 333     }
 334 
 335     public int getSoLinger() throws SocketException {
 336         return getIntOption(StandardSocketOptions.SO_LINGER);
 337     }
 338 
 339     public void sendUrgentData(int data) throws IOException {
 340         int n = sc.sendOutOfBandData((byte) data);
 341         if (n == 0)
 342             throw new IOException("Socket buffer full");
 343     }
 344 
 345     public void setOOBInline(boolean on) throws SocketException {
 346         setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
 347     }
 348 
 349     public boolean getOOBInline() throws SocketException {
 350         return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
 351     }
 352 
 353     public void setSoTimeout(int timeout) throws SocketException {
 354         if (timeout < 0)
 355             throw new IllegalArgumentException("timeout can't be negative");
 356         this.timeout = timeout;
 357     }
 358 
 359     public int getSoTimeout() throws SocketException {
 360         return timeout;
 361     }
 362 
 363     public void setSendBufferSize(int size) throws SocketException {
 364         // size 0 valid for SocketChannel, invalid for Socket
 365         if (size <= 0)
 366             throw new IllegalArgumentException("Invalid send size");
 367         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
 368     }
 369 
 370     public int getSendBufferSize() throws SocketException {
 371         return getIntOption(StandardSocketOptions.SO_SNDBUF);
 372     }
 373 
 374     public void setReceiveBufferSize(int size) throws SocketException {
 375         // size 0 valid for SocketChannel, invalid for Socket
 376         if (size <= 0)
 377             throw new IllegalArgumentException("Invalid receive size");
 378         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
 379     }
 380 
 381     public int getReceiveBufferSize() throws SocketException {
 382         return getIntOption(StandardSocketOptions.SO_RCVBUF);
 383     }
 384 
 385     public void setKeepAlive(boolean on) throws SocketException {
 386         setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on);
 387     }
 388 
 389     public boolean getKeepAlive() throws SocketException {
 390         return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE);
 391     }
 392 
 393     public void setTrafficClass(int tc) throws SocketException {
 394         setIntOption(StandardSocketOptions.IP_TOS, tc);
 395     }
 396 
 397     public int getTrafficClass() throws SocketException {
 398         return getIntOption(StandardSocketOptions.IP_TOS);
 399     }
 400 
 401     public void setReuseAddress(boolean on) throws SocketException {
 402         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
 403     }
 404 
 405     public boolean getReuseAddress() throws SocketException {
 406         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
 407     }
 408 
 409     public void close() throws IOException {
 410         sc.close();
 411     }
 412 
 413     public void shutdownInput() throws IOException {
 414         try {
 415             sc.shutdownInput();
 416         } catch (Exception x) {
 417             Net.translateException(x);
 418         }
 419     }
 420 
 421     public void shutdownOutput() throws IOException {
 422         try {
 423             sc.shutdownOutput();
 424         } catch (Exception x) {
 425             Net.translateException(x);
 426         }
 427     }
 428 
 429     public String toString() {
 430         if (sc.isConnected())
 431             return "Socket[addr=" + getInetAddress() +
 432                 ",port=" + getPort() +
 433                 ",localport=" + getLocalPort() + "]";
 434         return "Socket[unconnected]";
 435     }
 436 
 437     public boolean isConnected() {
 438         return sc.isConnected();
 439     }
 440 
 441     public boolean isBound() {
 442         return sc.localAddress() != null;
 443     }
 444 
 445     public boolean isClosed() {
 446         return !sc.isOpen();
 447     }
 448 
 449     public boolean isInputShutdown() {
 450         return !sc.isInputOpen();
 451     }
 452 
 453     public boolean isOutputShutdown() {
 454         return !sc.isOutputOpen();
 455     }
 456 
 457 }