1 /*
   2  * Copyright (c) 1995, 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 java.net;
  27 
  28 import java.io.FileDescriptor;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.OutputStream;
  32 
  33 import java.security.AccessController;
  34 import java.security.PrivilegedActionException;
  35 import java.security.PrivilegedExceptionAction;
  36 import java.util.Collections;
  37 import java.util.HashSet;
  38 import java.util.Objects;
  39 import java.util.Set;
  40 
  41 import sun.net.ConnectionResetException;
  42 import sun.net.NetHooks;
  43 import sun.net.PlatformSocketImpl;
  44 import sun.net.ResourceManager;
  45 import sun.net.ext.ExtendedSocketOptions;
  46 import sun.net.util.SocketExceptions;
  47 
  48 /**
  49  * Default Socket Implementation. This implementation does
  50  * not implement any security checks.
  51  * Note this class should <b>NOT</b> be public.
  52  *
  53  * @author  Steven B. Byrne
  54  */
  55 abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSocketImpl {
  56     /* instance variable for SO_TIMEOUT */
  57     int timeout;   // timeout in millisec
  58     // traffic class
  59     private int trafficClass;
  60 
  61     private boolean shut_rd = false;
  62     private boolean shut_wr = false;
  63 
  64     private SocketInputStream socketInputStream = null;
  65     private SocketOutputStream socketOutputStream = null;
  66 
  67     /* number of threads using the FileDescriptor */
  68     protected int fdUseCount = 0;
  69 
  70     /* lock when increment/decrementing fdUseCount */
  71     protected final Object fdLock = new Object();
  72 
  73     /* indicates a close is pending on the file descriptor */
  74     protected boolean closePending = false;
  75 
  76     /* indicates connection reset state */
  77     private volatile boolean connectionReset;
  78 
  79     /* indicates whether impl is bound  */
  80     boolean isBound;
  81 
  82     /* indicates whether impl is connected  */
  83     volatile boolean isConnected;
  84 
  85    /* whether this Socket is a stream (TCP) socket or not (UDP)
  86     */
  87     protected boolean stream;
  88 
  89     /* whether this is a server or not */
  90     final boolean isServer;
  91 
  92     /**
  93      * Load net library into runtime.
  94      */
  95     static {
  96         java.security.AccessController.doPrivileged(
  97             new java.security.PrivilegedAction<>() {
  98                 public Void run() {
  99                     System.loadLibrary("net");
 100                     return null;
 101                 }
 102             });
 103     }
 104 
 105     private static volatile boolean checkedReusePort;
 106     private static volatile boolean isReusePortAvailable;
 107 
 108     /**
 109      * Tells whether SO_REUSEPORT is supported.
 110      */
 111     static boolean isReusePortAvailable() {
 112         if (!checkedReusePort) {
 113             isReusePortAvailable = isReusePortAvailable0();
 114             checkedReusePort = true;
 115         }
 116         return isReusePortAvailable;
 117     }
 118 
 119     AbstractPlainSocketImpl(boolean isServer) {
 120         this.isServer = isServer;
 121     }
 122 
 123     /**
 124      * Creates a socket with a boolean that specifies whether this
 125      * is a stream socket (true) or an unconnected UDP socket (false).
 126      */
 127     protected synchronized void create(boolean stream) throws IOException {
 128         this.stream = stream;
 129         if (!stream) {
 130             ResourceManager.beforeUdpCreate();
 131             // only create the fd after we know we will be able to create the socket
 132             fd = new FileDescriptor();
 133             try {
 134                 socketCreate(false);
 135                 SocketCleanable.register(fd);
 136             } catch (IOException ioe) {
 137                 ResourceManager.afterUdpClose();
 138                 fd = null;
 139                 throw ioe;
 140             }
 141         } else {
 142             fd = new FileDescriptor();
 143             socketCreate(true);
 144             SocketCleanable.register(fd);
 145         }
 146     }
 147 
 148     /**
 149      * Creates a socket and connects it to the specified port on
 150      * the specified host.
 151      * @param host the specified host
 152      * @param port the specified port
 153      */
 154     protected void connect(String host, int port)
 155         throws UnknownHostException, IOException
 156     {
 157         boolean connected = false;
 158         try {
 159             InetAddress address = InetAddress.getByName(host);
 160             this.port = port;
 161             this.address = address;
 162 
 163             connectToAddress(address, port, timeout);
 164             connected = true;
 165         } finally {
 166             if (!connected) {
 167                 try {
 168                     close();
 169                 } catch (IOException ioe) {
 170                     /* Do nothing. If connect threw an exception then
 171                        it will be passed up the call stack */
 172                 }
 173             }
 174             isConnected = connected;
 175         }
 176     }
 177 
 178     /**
 179      * Creates a socket and connects it to the specified address on
 180      * the specified port.
 181      * @param address the address
 182      * @param port the specified port
 183      */
 184     protected void connect(InetAddress address, int port) throws IOException {
 185         this.port = port;
 186         this.address = address;
 187 
 188         try {
 189             connectToAddress(address, port, timeout);
 190             isConnected = true;
 191             return;
 192         } catch (IOException e) {
 193             // everything failed
 194             close();
 195             throw e;
 196         }
 197     }
 198 
 199     /**
 200      * Creates a socket and connects it to the specified address on
 201      * the specified port.
 202      * @param address the address
 203      * @param timeout the timeout value in milliseconds, or zero for no timeout.
 204      * @throws IOException if connection fails
 205      * @throws  IllegalArgumentException if address is null or is a
 206      *          SocketAddress subclass not supported by this socket
 207      * @since 1.4
 208      */
 209     protected void connect(SocketAddress address, int timeout)
 210             throws IOException {
 211         boolean connected = false;
 212         try {
 213             if (address == null || !(address instanceof InetSocketAddress))
 214                 throw new IllegalArgumentException("unsupported address type");
 215             InetSocketAddress addr = (InetSocketAddress) address;
 216             if (addr.isUnresolved())
 217                 throw new UnknownHostException(addr.getHostName());
 218             this.port = addr.getPort();
 219             this.address = addr.getAddress();
 220 
 221             connectToAddress(this.address, port, timeout);
 222             connected = true;
 223         } finally {
 224             if (!connected) {
 225                 try {
 226                     close();
 227                 } catch (IOException ioe) {
 228                     /* Do nothing. If connect threw an exception then
 229                        it will be passed up the call stack */
 230                 }
 231             }
 232             isConnected = connected;
 233         }
 234     }
 235 
 236     private void connectToAddress(InetAddress address, int port, int timeout) throws IOException {
 237         if (address.isAnyLocalAddress()) {
 238             doConnect(InetAddress.getLocalHost(), port, timeout);
 239         } else {
 240             doConnect(address, port, timeout);
 241         }
 242     }
 243 
 244     public void setOption(int opt, Object val) throws SocketException {
 245         if (isClosedOrPending()) {
 246             throw new SocketException("Socket Closed");
 247         }
 248         boolean on = true;
 249         switch (opt) {
 250             /* check type safety b4 going native.  These should never
 251              * fail, since only java.Socket* has access to
 252              * PlainSocketImpl.setOption().
 253              */
 254         case SO_LINGER:
 255             if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
 256                 throw new SocketException("Bad parameter for option");
 257             if (val instanceof Boolean) {
 258                 /* true only if disabling - enabling should be Integer */
 259                 on = false;
 260             }
 261             break;
 262         case SO_TIMEOUT:
 263             if (val == null || (!(val instanceof Integer)))
 264                 throw new SocketException("Bad parameter for SO_TIMEOUT");
 265             int tmp = ((Integer) val).intValue();
 266             if (tmp < 0)
 267                 throw new IllegalArgumentException("timeout < 0");
 268             timeout = tmp;
 269             break;
 270         case IP_TOS:
 271              if (val == null || !(val instanceof Integer)) {
 272                  throw new SocketException("bad argument for IP_TOS");
 273              }
 274              trafficClass = ((Integer)val).intValue();
 275              break;
 276         case SO_BINDADDR:
 277             throw new SocketException("Cannot re-bind socket");
 278         case TCP_NODELAY:
 279             if (val == null || !(val instanceof Boolean))
 280                 throw new SocketException("bad parameter for TCP_NODELAY");
 281             on = ((Boolean)val).booleanValue();
 282             break;
 283         case SO_SNDBUF:
 284         case SO_RCVBUF:
 285             if (val == null || !(val instanceof Integer) ||
 286                 !(((Integer)val).intValue() > 0)) {
 287                 throw new SocketException("bad parameter for SO_SNDBUF " +
 288                                           "or SO_RCVBUF");
 289             }
 290             break;
 291         case SO_KEEPALIVE:
 292             if (val == null || !(val instanceof Boolean))
 293                 throw new SocketException("bad parameter for SO_KEEPALIVE");
 294             on = ((Boolean)val).booleanValue();
 295             break;
 296         case SO_OOBINLINE:
 297             if (val == null || !(val instanceof Boolean))
 298                 throw new SocketException("bad parameter for SO_OOBINLINE");
 299             on = ((Boolean)val).booleanValue();
 300             break;
 301         case SO_REUSEADDR:
 302             if (val == null || !(val instanceof Boolean))
 303                 throw new SocketException("bad parameter for SO_REUSEADDR");
 304             on = ((Boolean)val).booleanValue();
 305             break;
 306         case SO_REUSEPORT:
 307             if (val == null || !(val instanceof Boolean))
 308                 throw new SocketException("bad parameter for SO_REUSEPORT");
 309             if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT))
 310                 throw new UnsupportedOperationException("unsupported option");
 311             on = ((Boolean)val).booleanValue();
 312             break;
 313         default:
 314             throw new SocketException("unrecognized TCP option: " + opt);
 315         }
 316         socketSetOption(opt, on, val);
 317     }
 318     public Object getOption(int opt) throws SocketException {
 319         if (isClosedOrPending()) {
 320             throw new SocketException("Socket Closed");
 321         }
 322         if (opt == SO_TIMEOUT) {
 323             return timeout;
 324         }
 325         int ret = 0;
 326         /*
 327          * The native socketGetOption() knows about 3 options.
 328          * The 32 bit value it returns will be interpreted according
 329          * to what we're asking.  A return of -1 means it understands
 330          * the option but its turned off.  It will raise a SocketException
 331          * if "opt" isn't one it understands.
 332          */
 333 
 334         switch (opt) {
 335         case TCP_NODELAY:
 336             ret = socketGetOption(opt, null);
 337             return Boolean.valueOf(ret != -1);
 338         case SO_OOBINLINE:
 339             ret = socketGetOption(opt, null);
 340             return Boolean.valueOf(ret != -1);
 341         case SO_LINGER:
 342             ret = socketGetOption(opt, null);
 343             return (ret == -1) ? Boolean.FALSE: (Object)(ret);
 344         case SO_REUSEADDR:
 345             ret = socketGetOption(opt, null);
 346             return Boolean.valueOf(ret != -1);
 347         case SO_BINDADDR:
 348             InetAddressContainer in = new InetAddressContainer();
 349             ret = socketGetOption(opt, in);
 350             return in.addr;
 351         case SO_SNDBUF:
 352         case SO_RCVBUF:
 353             ret = socketGetOption(opt, null);
 354             return ret;
 355         case IP_TOS:
 356             try {
 357                 ret = socketGetOption(opt, null);
 358                 if (ret == -1) { // ipv6 tos
 359                     return trafficClass;
 360                 } else {
 361                     return ret;
 362                 }
 363             } catch (SocketException se) {
 364                     // TODO - should make better effort to read TOS or TCLASS
 365                     return trafficClass; // ipv6 tos
 366             }
 367         case SO_KEEPALIVE:
 368             ret = socketGetOption(opt, null);
 369             return Boolean.valueOf(ret != -1);
 370         case SO_REUSEPORT:
 371             if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
 372                 throw new UnsupportedOperationException("unsupported option");
 373             }
 374             ret = socketGetOption(opt, null);
 375             return Boolean.valueOf(ret != -1);
 376         // should never get here
 377         default:
 378             return null;
 379         }
 380     }
 381 
 382     static final ExtendedSocketOptions extendedOptions =
 383             ExtendedSocketOptions.getInstance();
 384 
 385     private static final Set<SocketOption<?>> clientSocketOptions = clientSocketOptions();
 386     private static final Set<SocketOption<?>> serverSocketOptions = serverSocketOptions();
 387 
 388     private static Set<SocketOption<?>> clientSocketOptions() {
 389         HashSet<SocketOption<?>> options = new HashSet<>();
 390         options.add(StandardSocketOptions.SO_KEEPALIVE);
 391         options.add(StandardSocketOptions.SO_SNDBUF);
 392         options.add(StandardSocketOptions.SO_RCVBUF);
 393         options.add(StandardSocketOptions.SO_REUSEADDR);
 394         options.add(StandardSocketOptions.SO_LINGER);
 395         options.add(StandardSocketOptions.IP_TOS);
 396         options.add(StandardSocketOptions.TCP_NODELAY);
 397         if (isReusePortAvailable())
 398             options.add(StandardSocketOptions.SO_REUSEPORT);
 399         options.addAll(ExtendedSocketOptions.clientSocketOptions());
 400         return Collections.unmodifiableSet(options);
 401     }
 402 
 403     private static Set<SocketOption<?>> serverSocketOptions() {
 404         HashSet<SocketOption<?>> options = new HashSet<>();
 405         options.add(StandardSocketOptions.SO_RCVBUF);
 406         options.add(StandardSocketOptions.SO_REUSEADDR);
 407         options.add(StandardSocketOptions.IP_TOS);
 408         if (isReusePortAvailable())
 409             options.add(StandardSocketOptions.SO_REUSEPORT);
 410         options.addAll(ExtendedSocketOptions.serverSocketOptions());
 411         return Collections.unmodifiableSet(options);
 412     }
 413 
 414     @Override
 415     protected Set<SocketOption<?>> supportedOptions() {
 416         if (isServer)
 417             return serverSocketOptions;
 418         else
 419             return clientSocketOptions;
 420     }
 421 
 422     @Override
 423     protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
 424         Objects.requireNonNull(name);
 425         if (!supportedOptions().contains(name))
 426             throw new UnsupportedOperationException("'" + name + "' not supported");
 427 
 428         if (!name.type().isInstance(value))
 429             throw new IllegalArgumentException("Invalid value '" + value + "'");
 430 
 431         if (isClosedOrPending())
 432             throw new SocketException("Socket closed");
 433 
 434         if (name == StandardSocketOptions.SO_KEEPALIVE) {
 435             setOption(SocketOptions.SO_KEEPALIVE, value);
 436         } else if (name == StandardSocketOptions.SO_SNDBUF) {
 437             if (((Integer)value).intValue() < 0)
 438                 throw new IllegalArgumentException("Invalid send buffer size:" + value);
 439             setOption(SocketOptions.SO_SNDBUF, value);
 440         } else if (name == StandardSocketOptions.SO_RCVBUF) {
 441             if (((Integer)value).intValue() < 0)
 442                 throw new IllegalArgumentException("Invalid recv buffer size:" + value);
 443             setOption(SocketOptions.SO_RCVBUF, value);
 444         } else if (name == StandardSocketOptions.SO_REUSEADDR) {
 445             setOption(SocketOptions.SO_REUSEADDR, value);
 446         } else if (name == StandardSocketOptions.SO_REUSEPORT) {
 447             setOption(SocketOptions.SO_REUSEPORT, value);
 448         } else if (name == StandardSocketOptions.SO_LINGER ) {
 449             if (((Integer)value).intValue() < 0)
 450                 setOption(SocketOptions.SO_LINGER, false);
 451             else
 452                 setOption(SocketOptions.SO_LINGER, value);
 453         } else if (name == StandardSocketOptions.IP_TOS) {
 454             int i = ((Integer)value).intValue();
 455             if (i < 0 || i > 255)
 456                 throw new IllegalArgumentException("Invalid IP_TOS value: " + value);
 457             setOption(SocketOptions.IP_TOS, value);
 458         } else if (name == StandardSocketOptions.TCP_NODELAY) {
 459             setOption(SocketOptions.TCP_NODELAY, value);
 460         } else if (extendedOptions.isOptionSupported(name)) {
 461             extendedOptions.setOption(fd, name, value);
 462         } else {
 463             throw new AssertionError("unknown option: " + name);
 464         }
 465     }
 466 
 467     @Override
 468     @SuppressWarnings("unchecked")
 469     protected <T> T getOption(SocketOption<T> name) throws IOException {
 470         Objects.requireNonNull(name);
 471         if (!supportedOptions().contains(name))
 472             throw new UnsupportedOperationException("'" + name + "' not supported");
 473 
 474         if (isClosedOrPending())
 475             throw new SocketException("Socket closed");
 476 
 477         if (name == StandardSocketOptions.SO_KEEPALIVE) {
 478             return (T)getOption(SocketOptions.SO_KEEPALIVE);
 479         } else if (name == StandardSocketOptions.SO_SNDBUF) {
 480             return (T)getOption(SocketOptions.SO_SNDBUF);
 481         } else if (name == StandardSocketOptions.SO_RCVBUF) {
 482             return (T)getOption(SocketOptions.SO_RCVBUF);
 483         } else if (name == StandardSocketOptions.SO_REUSEADDR) {
 484             return (T)getOption(SocketOptions.SO_REUSEADDR);
 485         } else if (name == StandardSocketOptions.SO_REUSEPORT) {
 486             return (T)getOption(SocketOptions.SO_REUSEPORT);
 487         } else if (name == StandardSocketOptions.SO_LINGER) {
 488             Object value = getOption(SocketOptions.SO_LINGER);
 489             if (value instanceof Boolean) {
 490                 assert ((Boolean)value).booleanValue() == false;
 491                 value = -1;
 492             }
 493             return (T)value;
 494         } else if (name == StandardSocketOptions.IP_TOS) {
 495             return (T)getOption(SocketOptions.IP_TOS);
 496         } else if (name == StandardSocketOptions.TCP_NODELAY) {
 497             return (T)getOption(SocketOptions.TCP_NODELAY);
 498         } else if (extendedOptions.isOptionSupported(name)) {
 499             return (T) extendedOptions.getOption(fd, name);
 500         } else {
 501             throw new AssertionError("unknown option: " + name);
 502         }
 503     }
 504 
 505     /**
 506      * The workhorse of the connection operation.  Tries several times to
 507      * establish a connection to the given <host, port>.  If unsuccessful,
 508      * throws an IOException indicating what went wrong.
 509      */
 510 
 511     synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {
 512         synchronized (fdLock) {
 513             if (!closePending && !isBound) {
 514                 NetHooks.beforeTcpConnect(fd, address, port);
 515             }
 516         }
 517         try {
 518             acquireFD();
 519             try {
 520                 socketConnect(address, port, timeout);
 521                 /* socket may have been closed during poll/select */
 522                 synchronized (fdLock) {
 523                     if (closePending) {
 524                         throw new SocketException ("Socket closed");
 525                     }
 526                 }
 527             } finally {
 528                 releaseFD();
 529             }
 530         } catch (IOException e) {
 531             close();
 532             throw SocketExceptions.of(e, new InetSocketAddress(address, port));
 533         }
 534     }
 535 
 536     /**
 537      * Binds the socket to the specified address of the specified local port.
 538      * @param address the address
 539      * @param lport the port
 540      */
 541     protected synchronized void bind(InetAddress address, int lport)
 542         throws IOException
 543     {
 544        synchronized (fdLock) {
 545             if (!closePending && !isBound) {
 546                 NetHooks.beforeTcpBind(fd, address, lport);
 547             }
 548         }
 549         socketBind(address, lport);
 550         isBound = true;
 551     }
 552 
 553     /**
 554      * Listens, for a specified amount of time, for connections.
 555      * @param count the amount of time to listen for connections
 556      */
 557     protected synchronized void listen(int count) throws IOException {
 558         socketListen(count);
 559     }
 560 
 561     /**
 562      * Accepts connections.
 563      * @param si the socket impl
 564      */
 565     protected void accept(SocketImpl si) throws IOException {
 566         si.fd = new FileDescriptor();
 567         acquireFD();
 568         try {
 569             socketAccept(si);
 570         } finally {
 571             releaseFD();
 572         }
 573         SocketCleanable.register(si.fd);
 574     }
 575 
 576     /**
 577      * Gets an InputStream for this socket.
 578      */
 579     protected synchronized InputStream getInputStream() throws IOException {
 580         synchronized (fdLock) {
 581             if (isClosedOrPending())
 582                 throw new IOException("Socket Closed");
 583             if (shut_rd)
 584                 throw new IOException("Socket input is shutdown");
 585             if (socketInputStream == null) {
 586                 PrivilegedExceptionAction<SocketInputStream> pa = () -> new SocketInputStream(this);
 587                 try {
 588                     socketInputStream = AccessController.doPrivileged(pa);
 589                 } catch (PrivilegedActionException e) {
 590                     throw (IOException) e.getCause();
 591                 }
 592             }
 593         }
 594         return socketInputStream;
 595     }
 596 
 597     void setInputStream(SocketInputStream in) {
 598         socketInputStream = in;
 599     }
 600 
 601     /**
 602      * Gets an OutputStream for this socket.
 603      */
 604     protected synchronized OutputStream getOutputStream() throws IOException {
 605         synchronized (fdLock) {
 606             if (isClosedOrPending())
 607                 throw new IOException("Socket Closed");
 608             if (shut_wr)
 609                 throw new IOException("Socket output is shutdown");
 610             if (socketOutputStream == null) {
 611                 PrivilegedExceptionAction<SocketOutputStream> pa = () -> new SocketOutputStream(this);
 612                 try {
 613                     socketOutputStream = AccessController.doPrivileged(pa);
 614                 } catch (PrivilegedActionException e) {
 615                     throw (IOException) e.getCause();
 616                 }
 617             }
 618         }
 619         return socketOutputStream;
 620     }
 621 
 622     void setFileDescriptor(FileDescriptor fd) {
 623         this.fd = fd;
 624     }
 625 
 626     void setAddress(InetAddress address) {
 627         this.address = address;
 628     }
 629 
 630     void setPort(int port) {
 631         this.port = port;
 632     }
 633 
 634     void setLocalPort(int localport) {
 635         this.localport = localport;
 636     }
 637 
 638     /**
 639      * Returns the number of bytes that can be read without blocking.
 640      */
 641     protected synchronized int available() throws IOException {
 642         if (isClosedOrPending()) {
 643             throw new IOException("Stream closed.");
 644         }
 645 
 646         /*
 647          * If connection has been reset or shut down for input, then return 0
 648          * to indicate there are no buffered bytes.
 649          */
 650         if (isConnectionReset() || shut_rd) {
 651             return 0;
 652         }
 653 
 654         /*
 655          * If no bytes available and we were previously notified
 656          * of a connection reset then we move to the reset state.
 657          *
 658          * If are notified of a connection reset then check
 659          * again if there are bytes buffered on the socket.
 660          */
 661         int n = 0;
 662         try {
 663             n = socketAvailable();
 664         } catch (ConnectionResetException exc1) {
 665             setConnectionReset();
 666         }
 667         return n;
 668     }
 669 
 670     /**
 671      * Closes the socket.
 672      */
 673     protected void close() throws IOException {
 674         synchronized(fdLock) {
 675             if (fd != null) {
 676                 if (!stream) {
 677                     ResourceManager.afterUdpClose();
 678                 }
 679                 if (fdUseCount == 0) {
 680                     if (closePending) {
 681                         return;
 682                     }
 683                     closePending = true;
 684                     /*
 685                      * We close the FileDescriptor in two-steps - first the
 686                      * "pre-close" which closes the socket but doesn't
 687                      * release the underlying file descriptor. This operation
 688                      * may be lengthy due to untransmitted data and a long
 689                      * linger interval. Once the pre-close is done we do the
 690                      * actual socket to release the fd.
 691                      */
 692                     try {
 693                         socketPreClose();
 694                     } finally {
 695                         socketClose();
 696                     }
 697                     fd = null;
 698                     return;
 699                 } else {
 700                     /*
 701                      * If a thread has acquired the fd and a close
 702                      * isn't pending then use a deferred close.
 703                      * Also decrement fdUseCount to signal the last
 704                      * thread that releases the fd to close it.
 705                      */
 706                     if (!closePending) {
 707                         closePending = true;
 708                         fdUseCount--;
 709                         socketPreClose();
 710                     }
 711                 }
 712             }
 713         }
 714     }
 715 
 716     void reset() {
 717         throw new InternalError("should not get here");
 718     }
 719 
 720     /**
 721      * Shutdown read-half of the socket connection;
 722      */
 723     protected void shutdownInput() throws IOException {
 724       if (fd != null) {
 725           socketShutdown(SHUT_RD);
 726           if (socketInputStream != null) {
 727               socketInputStream.setEOF(true);
 728           }
 729           shut_rd = true;
 730       }
 731     }
 732 
 733     /**
 734      * Shutdown write-half of the socket connection;
 735      */
 736     protected void shutdownOutput() throws IOException {
 737       if (fd != null) {
 738           socketShutdown(SHUT_WR);
 739           shut_wr = true;
 740       }
 741     }
 742 
 743     protected boolean supportsUrgentData () {
 744         return true;
 745     }
 746 
 747     protected void sendUrgentData (int data) throws IOException {
 748         if (fd == null) {
 749             throw new IOException("Socket Closed");
 750         }
 751         socketSendUrgentData (data);
 752     }
 753 
 754     /*
 755      * "Acquires" and returns the FileDescriptor for this impl
 756      *
 757      * A corresponding releaseFD is required to "release" the
 758      * FileDescriptor.
 759      */
 760     FileDescriptor acquireFD() {
 761         synchronized (fdLock) {
 762             fdUseCount++;
 763             return fd;
 764         }
 765     }
 766 
 767     /*
 768      * "Release" the FileDescriptor for this impl.
 769      *
 770      * If the use count goes to -1 then the socket is closed.
 771      */
 772     void releaseFD() {
 773         synchronized (fdLock) {
 774             fdUseCount--;
 775             if (fdUseCount == -1) {
 776                 if (fd != null) {
 777                     try {
 778                         socketClose();
 779                     } catch (IOException e) {
 780                     } finally {
 781                         fd = null;
 782                     }
 783                 }
 784             }
 785         }
 786     }
 787 
 788     boolean isConnectionReset() {
 789         return connectionReset;
 790     }
 791 
 792     void setConnectionReset() {
 793         connectionReset = true;
 794     }
 795 
 796     /*
 797      * Return true if already closed or close is pending
 798      */
 799     public boolean isClosedOrPending() {
 800         /*
 801          * Lock on fdLock to ensure that we wait if a
 802          * close is in progress.
 803          */
 804         synchronized (fdLock) {
 805             if (closePending || (fd == null)) {
 806                 return true;
 807             } else {
 808                 return false;
 809             }
 810         }
 811     }
 812 
 813     /*
 814      * Return the current value of SO_TIMEOUT
 815      */
 816     public int getTimeout() {
 817         return timeout;
 818     }
 819 
 820     /*
 821      * "Pre-close" a socket by dup'ing the file descriptor - this enables
 822      * the socket to be closed without releasing the file descriptor.
 823      */
 824     private void socketPreClose() throws IOException {
 825         socketClose0(true);
 826     }
 827 
 828     /*
 829      * Close the socket (and release the file descriptor).
 830      */
 831     protected void socketClose() throws IOException {
 832         SocketCleanable.unregister(fd);
 833         socketClose0(false);
 834     }
 835 
 836     abstract void socketCreate(boolean stream) throws IOException;
 837     abstract void socketConnect(InetAddress address, int port, int timeout)
 838         throws IOException;
 839     abstract void socketBind(InetAddress address, int port)
 840         throws IOException;
 841     abstract void socketListen(int count)
 842         throws IOException;
 843     abstract void socketAccept(SocketImpl s)
 844         throws IOException;
 845     abstract int socketAvailable()
 846         throws IOException;
 847     abstract void socketClose0(boolean useDeferredClose)
 848         throws IOException;
 849     abstract void socketShutdown(int howto)
 850         throws IOException;
 851     abstract void socketSetOption(int cmd, boolean on, Object value)
 852         throws SocketException;
 853     abstract int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
 854     abstract void socketSendUrgentData(int data)
 855         throws IOException;
 856 
 857     public static final int SHUT_RD = 0;
 858     public static final int SHUT_WR = 1;
 859 
 860     private static native boolean isReusePortAvailable0();
 861 }