/* |
|
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package sun.security.ssl; |
|
import java.io.*; |
|
import java.net.*; |
|
import java.nio.channels.SocketChannel; |
|
import java.util.Set; |
|
import javax.net.ssl.*; |
|
/** |
|
* Abstract base class for SSLSocketImpl. |
|
* |
|
* Its purpose is to house code with no SSL related logic (or no logic at all). |
|
* This makes SSLSocketImpl shorter and easier to read. It contains a few |
|
* constants and static methods plus overridden java.net.Socket methods. |
|
* |
|
* Methods are defined final to ensure that they are not accidentally |
|
* overridden in SSLSocketImpl. |
|
* |
|
* @see javax.net.ssl.SSLSocket |
|
* @see SSLSocketImpl |
|
*/ |
|
abstract class BaseSSLSocketImpl extends SSLSocket { |
|
/* |
|
* Normally "self" is "this" ... but not when this connection is |
|
* layered over a preexisting socket. If we're using an existing |
|
* socket, we delegate some actions to it. Else, we delegate |
|
* instead to "super". This is important to ensure that we don't |
|
* recurse infinitely ... e.g. close() calling itself, or doing |
|
* I/O in terms of our own streams. |
|
*/ |
|
private final Socket self; |
|
private final InputStream consumedInput; |
|
BaseSSLSocketImpl() { |
|
super(); |
|
this.self = this; |
|
this.consumedInput = null; |
|
} |
|
BaseSSLSocketImpl(Socket socket) { |
|
super(); |
|
this.self = socket; |
|
this.consumedInput = null; |
|
} |
|
BaseSSLSocketImpl(Socket socket, InputStream consumed) { |
|
super(); |
|
this.self = socket; |
|
this.consumedInput = consumed; |
|
} |
|
// |
|
// CONSTANTS AND STATIC METHODS |
|
// |
|
/** |
|
* TLS requires that a close_notify warning alert is sent before the |
|
* connection is closed in order to avoid truncation attacks. Some |
|
* implementations (MS IIS and others) don't do that. The property |
|
* below controls whether we accept that or treat it as an error. |
|
* |
|
* The default is "false", i.e. tolerate the broken behavior. |
|
*/ |
|
private static final String PROP_NAME = |
|
"com.sun.net.ssl.requireCloseNotify"; |
|
static final boolean requireCloseNotify = |
|
Utilities.getBooleanProperty(PROP_NAME, false); |
|
// |
|
// MISC SOCKET METHODS |
|
// |
|
/** |
|
* Returns the unique {@link java.nio.SocketChannel SocketChannel} object |
|
* associated with this socket, if any. |
|
* @see java.net.Socket#getChannel |
|
*/ |
|
@Override |
|
public final SocketChannel getChannel() { |
|
if (self == this) { |
|
return super.getChannel(); |
|
} else { |
|
return self.getChannel(); |
|
} |
|
} |
|
/** |
|
* Binds the address to the socket. |
|
* @see java.net.Socket#bind |
|
*/ |
|
@Override |
|
public void bind(SocketAddress bindpoint) throws IOException { |
|
/* |
|
* Bind to this socket |
|
*/ |
|
if (self == this) { |
|
super.bind(bindpoint); |
|
} else { |
|
// If we're binding on a layered socket... |
|
throw new IOException( |
|
"Underlying socket should already be connected"); |
|
} |
|
} |
|
/** |
|
* Returns the address of the endpoint this socket is connected to |
|
* @see java.net.Socket#getLocalSocketAddress |
|
*/ |
|
@Override |
|
public SocketAddress getLocalSocketAddress() { |
|
if (self == this) { |
|
return super.getLocalSocketAddress(); |
|
} else { |
|
return self.getLocalSocketAddress(); |
|
} |
|
} |
|
/** |
|
* Returns the address of the endpoint this socket is connected to |
|
* @see java.net.Socket#getRemoteSocketAddress |
|
*/ |
|
@Override |
|
public SocketAddress getRemoteSocketAddress() { |
|
if (self == this) { |
|
return super.getRemoteSocketAddress(); |
|
} else { |
|
return self.getRemoteSocketAddress(); |
|
} |
|
} |
|
/** |
|
* Connects this socket to the server. |
|
* |
|
* This method is either called on an unconnected SSLSocketImpl by the |
|
* application, or it is called in the constructor of a regular |
|
* SSLSocketImpl. If we are layering on top on another socket, then |
|
* this method should not be called, because we assume that the |
|
* underlying socket is already connected by the time it is passed to |
|
* us. |
|
* |
|
* @param endpoint the <code>SocketAddress</code> |
|
* @throws IOException if an error occurs during the connection |
|
*/ |
|
@Override |
|
public final void connect(SocketAddress endpoint) throws IOException { |
|
connect(endpoint, 0); |
|
} |
|
/** |
|
* Returns the connection state of the socket. |
|
* @see java.net.Socket#isConnected |
|
*/ |
|
@Override |
|
public final boolean isConnected() { |
|
if (self == this) { |
|
return super.isConnected(); |
|
} else { |
|
return self.isConnected(); |
|
} |
|
} |
|
/** |
|
* Returns the binding state of the socket. |
|
* @see java.net.Socket#isBound |
|
*/ |
|
@Override |
|
public final boolean isBound() { |
|
if (self == this) { |
|
return super.isBound(); |
|
} else { |
|
return self.isBound(); |
|
} |
|
} |
|
// |
|
// CLOSE RELATED METHODS |
|
// |
|
/** |
|
* Places the input stream for this socket at "end of stream". Any data |
|
* sent to the input stream side of the socket is acknowledged and then |
|
* silently discarded. |
|
* |
|
* @see java.net.Socket#shutdownInput |
|
*/ |
|
@Override |
|
public void shutdownInput() throws IOException { |
|
if (self == this) { |
|
super.shutdownInput(); |
|
} else { |
|
self.shutdownInput(); |
|
} |
|
} |
|
/** |
|
* Disables the output stream for this socket. For a TCP socket, any |
|
* previously written data will be sent followed by TCP's normal |
|
* connection termination sequence. |
|
* |
|
* @see java.net.Socket#shutdownOutput |
|
*/ |
|
@Override |
|
public void shutdownOutput() throws IOException { |
|
if (self == this) { |
|
super.shutdownOutput(); |
|
} else { |
|
self.shutdownOutput(); |
|
} |
|
} |
|
/** |
|
* Returns the input state of the socket |
|
* @see java.net.Socket#isInputShutdown |
|
*/ |
|
@Override |
|
public boolean isInputShutdown() { |
|
if (self == this) { |
|
return super.isInputShutdown(); |
|
} else { |
|
return self.isInputShutdown(); |
|
} |
|
} |
|
/** |
|
* Returns the output state of the socket |
|
* @see java.net.Socket#isOutputShutdown |
|
*/ |
|
@Override |
|
public boolean isOutputShutdown() { |
|
if (self == this) { |
|
return super.isOutputShutdown(); |
|
} else { |
|
return self.isOutputShutdown(); |
|
} |
|
} |
|
/** |
|
* Ensures that the SSL connection is closed down as cleanly |
|
* as possible, in case the application forgets to do so. |
|
* This allows SSL connections to be implicitly reclaimed, |
|
* rather than forcing them to be explicitly reclaimed at |
|
* the penalty of prematurly killing SSL sessions. |
|
*/ |
|
@Override |
|
@SuppressWarnings("deprecation") |
|
protected final void finalize() throws Throwable { |
|
try { |
|
close(); |
|
} catch (IOException e1) { |
|
try { |
|
if (self == this) { |
|
super.close(); |
|
} |
|
} catch (IOException e2) { |
|
// ignore |
|
} |
|
} finally { |
|
// We called close on the underlying socket above to |
|
// make doubly sure all resources got released. We |
|
// don't finalize self in the case of overlain sockets, |
|
// that's a different object which the GC will finalize |
|
// separately. |
|
super.finalize(); |
|
} |
|
} |
|
// |
|
// GET ADDRESS METHODS |
|
// |
|
/** |
|
* Returns the address of the remote peer for this connection. |
|
*/ |
|
@Override |
|
public final InetAddress getInetAddress() { |
|
if (self == this) { |
|
return super.getInetAddress(); |
|
} else { |
|
return self.getInetAddress(); |
|
} |
|
} |
|
/** |
|
* Gets the local address to which the socket is bound. |
|
* |
|
* @return the local address to which the socket is bound. |
|
* @since 1.1 |
|
*/ |
|
@Override |
|
public final InetAddress getLocalAddress() { |
|
if (self == this) { |
|
return super.getLocalAddress(); |
|
} else { |
|
return self.getLocalAddress(); |
|
} |
|
} |
|
/** |
|
* Returns the number of the remote port that this connection uses. |
|
*/ |
|
@Override |
|
public final int getPort() { |
|
if (self == this) { |
|
return super.getPort(); |
|
} else { |
|
return self.getPort(); |
|
} |
|
} |
|
/** |
|
* Returns the number of the local port that this connection uses. |
|
*/ |
|
@Override |
|
public final int getLocalPort() { |
|
if (self == this) { |
|
return super.getLocalPort(); |
|
} else { |
|
return self.getLocalPort(); |
|
} |
|
} |
|
// |
|
// SOCKET OPTION METHODS |
|
// |
|
/** |
|
* Enables or disables the Nagle optimization. |
|
* @see java.net.Socket#setTcpNoDelay |
|
*/ |
|
@Override |
|
public final void setTcpNoDelay(boolean value) throws SocketException { |
|
if (self == this) { |
|
super.setTcpNoDelay(value); |
|
} else { |
|
self.setTcpNoDelay(value); |
|
} |
|
} |
|
/** |
|
* Returns true if the Nagle optimization is disabled. This |
|
* relates to low-level buffering of TCP traffic, delaying the |
|
* traffic to promote better throughput. |
|
* |
|
* @see java.net.Socket#getTcpNoDelay |
|
*/ |
|
@Override |
|
public final boolean getTcpNoDelay() throws SocketException { |
|
if (self == this) { |
|
return super.getTcpNoDelay(); |
|
} else { |
|
return self.getTcpNoDelay(); |
|
} |
|
} |
|
/** |
|
* Assigns the socket's linger timeout. |
|
* @see java.net.Socket#setSoLinger |
|
*/ |
|
@Override |
|
public final void setSoLinger(boolean flag, int linger) |
|
throws SocketException { |
|
if (self == this) { |
|
super.setSoLinger(flag, linger); |
|
} else { |
|
self.setSoLinger(flag, linger); |
|
} |
|
} |
|
/** |
|
* Returns the socket's linger timeout. |
|
* @see java.net.Socket#getSoLinger |
|
*/ |
|
@Override |
|
public final int getSoLinger() throws SocketException { |
|
if (self == this) { |
|
return super.getSoLinger(); |
|
} else { |
|
return self.getSoLinger(); |
|
} |
|
} |
|
/** |
|
* Send one byte of urgent data on the socket. |
|
* @see java.net.Socket#sendUrgentData |
|
* At this point, there seems to be no specific requirement to support |
|
* this for an SSLSocket. An implementation can be provided if a need |
|
* arises in future. |
|
*/ |
|
@Override |
|
public final void sendUrgentData(int data) throws SocketException { |
|
throw new SocketException("This method is not supported " |
|
+ "by SSLSockets"); |
|
} |
|
/** |
|
* Enable/disable OOBINLINE (receipt of TCP urgent data) By default, this |
|
* option is disabled and TCP urgent data received on a socket is silently |
|
* discarded. |
|
* @see java.net.Socket#setOOBInline |
|
* Setting OOBInline does not have any effect on SSLSocket, |
|
* since currently we don't support sending urgent data. |
|
*/ |
|
@Override |
|
public final void setOOBInline(boolean on) throws SocketException { |
|
throw new SocketException("This method is ineffective, since" |
|
+ " sending urgent data is not supported by SSLSockets"); |
|
} |
|
/** |
|
* Tests if OOBINLINE is enabled. |
|
* @see java.net.Socket#getOOBInline |
|
*/ |
|
@Override |
|
public final boolean getOOBInline() throws SocketException { |
|
throw new SocketException("This method is ineffective, since" |
|
+ " sending urgent data is not supported by SSLSockets"); |
|
} |
|
/** |
|
* Returns the socket timeout. |
|
* @see java.net.Socket#getSoTimeout |
|
*/ |
|
@Override |
|
public final int getSoTimeout() throws SocketException { |
|
if (self == this) { |
|
return super.getSoTimeout(); |
|
} else { |
|
return self.getSoTimeout(); |
|
} |
|
} |
|
@Override |
|
public final void setSendBufferSize(int size) throws SocketException { |
|
if (self == this) { |
|
super.setSendBufferSize(size); |
|
} else { |
|
self.setSendBufferSize(size); |
|
} |
|
} |
|
@Override |
|
public final int getSendBufferSize() throws SocketException { |
|
if (self == this) { |
|
return super.getSendBufferSize(); |
|
} else { |
|
return self.getSendBufferSize(); |
|
} |
|
} |
|
@Override |
|
public final void setReceiveBufferSize(int size) throws SocketException { |
|
if (self == this) { |
|
super.setReceiveBufferSize(size); |
|
} else { |
|
self.setReceiveBufferSize(size); |
|
} |
|
} |
|
@Override |
|
public final int getReceiveBufferSize() throws SocketException { |
|
if (self == this) { |
|
return super.getReceiveBufferSize(); |
|
} else { |
|
return self.getReceiveBufferSize(); |
|
} |
|
} |
|
/** |
|
* Enable/disable SO_KEEPALIVE. |
|
* @see java.net.Socket#setKeepAlive |
|
*/ |
|
@Override |
|
public final void setKeepAlive(boolean on) throws SocketException { |
|
if (self == this) { |
|
super.setKeepAlive(on); |
|
} else { |
|
self.setKeepAlive(on); |
|
} |
|
} |
|
/** |
|
* Tests if SO_KEEPALIVE is enabled. |
|
* @see java.net.Socket#getKeepAlive |
|
*/ |
|
@Override |
|
public final boolean getKeepAlive() throws SocketException { |
|
if (self == this) { |
|
return super.getKeepAlive(); |
|
} else { |
|
return self.getKeepAlive(); |
|
} |
|
} |
|
/** |
|
* Sets traffic class or type-of-service octet in the IP header for |
|
* packets sent from this Socket. |
|
* @see java.net.Socket#setTrafficClass |
|
*/ |
|
@Override |
|
public final void setTrafficClass(int tc) throws SocketException { |
|
if (self == this) { |
|
super.setTrafficClass(tc); |
|
} else { |
|
self.setTrafficClass(tc); |
|
} |
|
} |
|
/** |
|
* Gets traffic class or type-of-service in the IP header for packets |
|
* sent from this Socket. |
|
* @see java.net.Socket#getTrafficClass |
|
*/ |
|
@Override |
|
public final int getTrafficClass() throws SocketException { |
|
if (self == this) { |
|
return super.getTrafficClass(); |
|
} else { |
|
return self.getTrafficClass(); |
|
} |
|
} |
|
/** |
|
* Enable/disable SO_REUSEADDR. |
|
* @see java.net.Socket#setReuseAddress |
|
*/ |
|
@Override |
|
public final void setReuseAddress(boolean on) throws SocketException { |
|
if (self == this) { |
|
super.setReuseAddress(on); |
|
} else { |
|
self.setReuseAddress(on); |
|
} |
|
} |
|
/** |
|
* Tests if SO_REUSEADDR is enabled. |
|
* @see java.net.Socket#getReuseAddress |
|
*/ |
|
@Override |
|
public final boolean getReuseAddress() throws SocketException { |
|
if (self == this) { |
|
return super.getReuseAddress(); |
|
} else { |
|
return self.getReuseAddress(); |
|
} |
|
} |
|
/** |
|
* Sets performance preferences for this socket. |
|
* |
|
* @see java.net.Socket#setPerformancePreferences(int, int, int) |
|
*/ |
|
@Override |
|
public void setPerformancePreferences(int connectionTime, |
|
int latency, int bandwidth) { |
|
if (self == this) { |
|
super.setPerformancePreferences( |
|
connectionTime, latency, bandwidth); |
|
} else { |
|
self.setPerformancePreferences( |
|
connectionTime, latency, bandwidth); |
|
} |
|
} |
|
@Override |
|
public String toString() { |
|
if (self == this) { |
|
return super.toString(); |
|
} |
|
return self.toString(); |
|
} |
|
@Override |
|
public InputStream getInputStream() throws IOException { |
|
if (self == this) { |
|
return super.getInputStream(); |
|
} |
|
if (consumedInput != null) { |
|
return new SequenceInputStream(consumedInput, |
|
self.getInputStream()); |
|
} |
|
return self.getInputStream(); |
|
} |
|
@Override |
|
public OutputStream getOutputStream() throws IOException { |
|
if (self == this) { |
|
return super.getOutputStream(); |
|
} |
|
return self.getOutputStream(); |
|
} |
|
@Override |
|
public void close() throws IOException { |
|
if (self == this) { |
|
super.close(); |
|
} else { |
|
self.close(); |
|
} |
|
} |
|
@Override |
|
public synchronized void setSoTimeout(int timeout) throws SocketException { |
|
if (self == this) { |
|
super.setSoTimeout(timeout); |
|
} else { |
|
self.setSoTimeout(timeout); |
|
} |
|
} |
|
boolean isLayered() { |
|
return (self != this); |
|
} |
|
} |