summaryrefslogtreecommitdiff
path: root/gnu/javax/net/ssl/provider/SSLSocketImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/javax/net/ssl/provider/SSLSocketImpl.java')
-rw-r--r--gnu/javax/net/ssl/provider/SSLSocketImpl.java833
1 files changed, 833 insertions, 0 deletions
diff --git a/gnu/javax/net/ssl/provider/SSLSocketImpl.java b/gnu/javax/net/ssl/provider/SSLSocketImpl.java
new file mode 100644
index 000000000..0181b66d8
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/SSLSocketImpl.java
@@ -0,0 +1,833 @@
+/* SSLSocketImpl.java -- implementation of an SSL client socket.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.net.ssl.provider;
+
+import gnu.classpath.debug.Component;
+import gnu.classpath.debug.SystemLogger;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class SSLSocketImpl extends SSLSocket
+{
+ private class SocketOutputStream extends OutputStream
+ {
+ private final ByteBuffer buffer;
+ private final OutputStream out;
+
+ SocketOutputStream() throws IOException
+ {
+ buffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ if (underlyingSocket != null)
+ out = underlyingSocket.getOutputStream();
+ else
+ out = SSLSocketImpl.super.getOutputStream();
+ }
+
+ @Override public void write(byte[] buf, int off, int len) throws IOException
+ {
+ if (!initialHandshakeDone
+ || engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING)
+ {
+ doHandshake();
+ if (handshakeException != null)
+ throw handshakeException;
+ }
+
+ int k = 0;
+ while (k < len)
+ {
+ synchronized (engine)
+ {
+ int l = Math.min(len-k, getSession().getApplicationBufferSize());
+ ByteBuffer in = ByteBuffer.wrap(buf, off+k, l);
+ SSLEngineResult result = engine.wrap(in, buffer);
+ if (result.getStatus() == Status.CLOSED)
+ return;
+ if (result.getStatus() != Status.OK)
+ throw new SSLException("unexpected SSL state " + result.getStatus());
+ buffer.flip();
+ out.write(buffer.array(), 0, buffer.limit());
+ k += result.bytesConsumed();
+ buffer.clear();
+ }
+ }
+ }
+
+ @Override public void write(int b) throws IOException
+ {
+ write(new byte[] { (byte) b });
+ }
+
+ @Override public void close() throws IOException
+ {
+ SSLSocketImpl.this.close();
+ }
+ }
+
+ private class SocketInputStream extends InputStream
+ {
+ private final ByteBuffer inBuffer;
+ private final ByteBuffer appBuffer;
+ private final DataInputStream in;
+
+ SocketInputStream() throws IOException
+ {
+ inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ inBuffer.limit(0);
+ appBuffer = ByteBuffer.allocate(getSession().getApplicationBufferSize());
+ appBuffer.flip();
+ if (underlyingSocket != null)
+ in = new DataInputStream(underlyingSocket.getInputStream());
+ else
+ in = new DataInputStream(SSLSocketImpl.super.getInputStream());
+ }
+
+ @Override public int read(byte[] buf, int off, int len) throws IOException
+ {
+ if (!initialHandshakeDone ||
+ engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING)
+ {
+ doHandshake();
+ if (handshakeException != null)
+ throw handshakeException;
+ }
+
+ if (!appBuffer.hasRemaining())
+ {
+ int x = in.read();
+ if (x == -1)
+ return -1;
+ inBuffer.clear();
+ inBuffer.put((byte) x);
+ inBuffer.putInt(in.readInt());
+ int reclen = inBuffer.getShort(3) & 0xFFFF;
+ in.readFully(inBuffer.array(), 5, reclen);
+ inBuffer.position(0).limit(reclen + 5);
+ synchronized (engine)
+ {
+ appBuffer.clear();
+ SSLEngineResult result = engine.unwrap(inBuffer, appBuffer);
+ Status status = result.getStatus();
+ if (status == Status.CLOSED && result.bytesProduced() == 0)
+ return -1;
+ }
+ inBuffer.compact();
+ appBuffer.flip();
+ }
+ int l = Math.min(len, appBuffer.remaining());
+ appBuffer.get(buf, off, l);
+ return l;
+ }
+
+ @Override public int read() throws IOException
+ {
+ byte[] b = new byte[1];
+ if (read(b) == -1)
+ return -1;
+ return b[0] & 0xFF;
+ }
+ }
+
+ private static final SystemLogger logger = SystemLogger.getSystemLogger();
+
+ private SSLEngineImpl engine;
+ private Set<HandshakeCompletedListener> listeners;
+ private Socket underlyingSocket;
+ private boolean isHandshaking;
+ private IOException handshakeException;
+ private boolean initialHandshakeDone = false;
+ private final boolean autoClose;
+
+ public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port)
+ {
+ this(contextImpl, host, port, null, false);
+ }
+
+ public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port,
+ Socket underlyingSocket, boolean autoClose)
+ {
+ engine = new SSLEngineImpl(contextImpl, host, port);
+ engine.setUseClientMode(true); // default to client mode
+ listeners = new HashSet<HandshakeCompletedListener>();
+ this.underlyingSocket = underlyingSocket;
+ this.autoClose = autoClose;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener)
+ */
+ @Override
+ public void addHandshakeCompletedListener(HandshakeCompletedListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getEnableSessionCreation()
+ */
+ @Override public boolean getEnableSessionCreation()
+ {
+ return engine.getEnableSessionCreation();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites()
+ */
+ @Override public String[] getEnabledCipherSuites()
+ {
+ return engine.getEnabledCipherSuites();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getEnabledProtocols()
+ */
+ @Override public String[] getEnabledProtocols()
+ {
+ return engine.getEnabledProtocols();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getNeedClientAuth()
+ */
+ @Override public boolean getNeedClientAuth()
+ {
+ return engine.getNeedClientAuth();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getSession()
+ */
+ @Override public SSLSession getSession()
+ {
+ return engine.getSession();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites()
+ */
+ @Override public String[] getSupportedCipherSuites()
+ {
+ return engine.getSupportedCipherSuites();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getSupportedProtocols()
+ */
+ @Override public String[] getSupportedProtocols()
+ {
+ return engine.getSupportedProtocols();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getUseClientMode()
+ */
+ @Override public boolean getUseClientMode()
+ {
+ return engine.getUseClientMode();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getWantClientAuth()
+ */
+ @Override public boolean getWantClientAuth()
+ {
+ return engine.getWantClientAuth();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener)
+ */
+ @Override
+ public void removeHandshakeCompletedListener(HandshakeCompletedListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean)
+ */
+ @Override public void setEnableSessionCreation(boolean enable)
+ {
+ engine.setEnableSessionCreation(enable);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(java.lang.String[])
+ */
+ @Override public void setEnabledCipherSuites(String[] suites)
+ {
+ engine.setEnabledCipherSuites(suites);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setEnabledProtocols(java.lang.String[])
+ */
+ @Override public void setEnabledProtocols(String[] protocols)
+ {
+ engine.setEnabledProtocols(protocols);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean)
+ */
+ @Override public void setNeedClientAuth(boolean needAuth)
+ {
+ engine.setNeedClientAuth(needAuth);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean)
+ */
+ @Override public void setUseClientMode(boolean clientMode)
+ {
+ engine.setUseClientMode(clientMode);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean)
+ */
+ @Override public void setWantClientAuth(boolean wantAuth)
+ {
+ engine.setWantClientAuth(wantAuth);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#startHandshake()
+ */
+ @Override public void startHandshake() throws IOException
+ {
+ if (isHandshaking)
+ return;
+
+ if (handshakeException != null)
+ throw handshakeException;
+
+ Thread t = new Thread(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ doHandshake();
+ }
+ catch (IOException ioe)
+ {
+ handshakeException = ioe;
+ }
+ }
+ }, "HandshakeThread@" + System.identityHashCode(this));
+ t.start();
+ }
+
+ void doHandshake() throws IOException
+ {
+ synchronized (engine)
+ {
+ if (isHandshaking)
+ {
+ try
+ {
+ engine.wait();
+ }
+ catch (InterruptedException ie)
+ {
+ }
+ return;
+ }
+ isHandshaking = true;
+ }
+
+ if (initialHandshakeDone)
+ throw new SSLException("rehandshaking not yet implemented");
+
+ long now = -System.currentTimeMillis();
+ engine.beginHandshake();
+
+ HandshakeStatus status = engine.getHandshakeStatus();
+ assert(status != HandshakeStatus.NOT_HANDSHAKING);
+
+ ByteBuffer inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ inBuffer.position(inBuffer.limit());
+ ByteBuffer outBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
+ SSLEngineResult result = null;
+
+ DataInputStream sockIn = null;
+ if (underlyingSocket != null)
+ sockIn = new DataInputStream(underlyingSocket.getInputStream());
+ else
+ sockIn = new DataInputStream(super.getInputStream());
+
+ OutputStream sockOut = null;
+ if (underlyingSocket != null)
+ sockOut = underlyingSocket.getOutputStream();
+ else
+ sockOut = super.getOutputStream();
+
+ try
+ {
+ while (status != HandshakeStatus.NOT_HANDSHAKING
+ && status != HandshakeStatus.FINISHED)
+ {
+ logger.logv(Component.SSL_HANDSHAKE, "socket processing state {0}",
+ status);
+
+ if (inBuffer.capacity() != getSession().getPacketBufferSize())
+ {
+ ByteBuffer b
+ = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ if (inBuffer.hasRemaining())
+ b.put(inBuffer).flip();
+ inBuffer = b;
+ }
+ if (outBuffer.capacity() != getSession().getPacketBufferSize())
+ outBuffer
+ = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+
+ switch (status)
+ {
+ case NEED_UNWRAP:
+ // Read in a single SSL record.
+ inBuffer.clear();
+ int i = sockIn.read();
+ if (i == -1)
+ throw new EOFException();
+ if ((i & 0x80) == 0x80) // SSLv2 client hello.
+ {
+ inBuffer.put((byte) i);
+ int v2len = (i & 0x7f) << 8;
+ i = sockIn.read();
+ v2len = v2len | (i & 0xff);
+ inBuffer.put((byte) i);
+ sockIn.readFully(inBuffer.array(), 2, v2len);
+ inBuffer.position(0).limit(v2len + 2);
+ }
+ else
+ {
+ inBuffer.put((byte) i);
+ inBuffer.putInt(sockIn.readInt());
+ int reclen = inBuffer.getShort(3) & 0xFFFF;
+ sockIn.readFully(inBuffer.array(), 5, reclen);
+ inBuffer.position(0).limit(reclen + 5);
+ }
+ result = engine.unwrap(inBuffer, emptyBuffer);
+ status = result.getHandshakeStatus();
+ if (result.getStatus() != Status.OK)
+ throw new SSLException("unexpected SSL status "
+ + result.getStatus());
+ break;
+
+ case NEED_WRAP:
+ {
+ outBuffer.clear();
+ result = engine.wrap(emptyBuffer, outBuffer);
+ status = result.getHandshakeStatus();
+ if (result.getStatus() != Status.OK)
+ throw new SSLException("unexpected SSL status "
+ + result.getStatus());
+ outBuffer.flip();
+ sockOut.write(outBuffer.array(), outBuffer.position(),
+ outBuffer.limit());
+ }
+ break;
+
+ case NEED_TASK:
+ {
+ Runnable task;
+ while ((task = engine.getDelegatedTask()) != null)
+ task.run();
+ status = engine.getHandshakeStatus();
+ }
+ break;
+
+ case FINISHED:
+ break;
+ }
+ }
+
+ initialHandshakeDone = true;
+
+ HandshakeCompletedEvent hce = new HandshakeCompletedEvent(this, getSession());
+ for (HandshakeCompletedListener l : listeners)
+ {
+ try
+ {
+ l.handshakeCompleted(hce);
+ }
+ catch (ThreadDeath td)
+ {
+ throw td;
+ }
+ catch (Throwable x)
+ {
+ logger.log(Component.WARNING,
+ "HandshakeCompletedListener threw exception", x);
+ }
+ }
+
+ now += System.currentTimeMillis();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE,
+ "handshake completed in {0}ms in thread {1}", now,
+ Thread.currentThread().getName());
+ }
+ catch (SSLException ssle)
+ {
+ handshakeException = ssle;
+ throw ssle;
+ }
+ finally
+ {
+ synchronized (engine)
+ {
+ isHandshaking = false;
+ engine.notifyAll();
+ }
+ }
+ }
+
+ // Methods overriding Socket.
+
+ @Override public void bind(SocketAddress bindpoint) throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.bind(bindpoint);
+ else
+ super.bind(bindpoint);
+ }
+
+ @Override public void connect(SocketAddress endpoint) throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.connect(endpoint);
+ else
+ super.connect(endpoint);
+ }
+
+ @Override public void connect(SocketAddress endpoint, int timeout)
+ throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.connect(endpoint, timeout);
+ else
+ super.connect(endpoint, timeout);
+ }
+
+ @Override public InetAddress getInetAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getInetAddress();
+ return super.getInetAddress();
+ }
+
+ @Override public InetAddress getLocalAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getLocalAddress();
+ return super.getLocalAddress();
+ }
+
+ @Override public int getPort()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getPort();
+ return super.getPort();
+ }
+
+ @Override public int getLocalPort()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getLocalPort();
+ return super.getLocalPort();
+ }
+
+ @Override public SocketAddress getRemoteSocketAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getRemoteSocketAddress();
+ return super.getRemoteSocketAddress();
+ }
+
+ public SocketAddress getLocalSocketAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getLocalSocketAddress();
+ return super.getLocalSocketAddress();
+ }
+
+ @Override public SocketChannel getChannel()
+ {
+ throw new UnsupportedOperationException("use javax.net.ssl.SSLEngine for NIO");
+ }
+
+ @Override public InputStream getInputStream() throws IOException
+ {
+ return new SocketInputStream();
+ }
+
+ @Override public OutputStream getOutputStream() throws IOException
+ {
+ return new SocketOutputStream();
+ }
+
+ @Override public void setTcpNoDelay(boolean on) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setTcpNoDelay(on);
+ else
+ super.setTcpNoDelay(on);
+ }
+
+ @Override public boolean getTcpNoDelay() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getTcpNoDelay();
+ return super.getTcpNoDelay();
+ }
+
+ @Override public void setSoLinger(boolean on, int linger) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setSoLinger(on, linger);
+ else
+ super.setSoLinger(on, linger);
+ }
+
+ public int getSoLinger() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getSoLinger();
+ return super.getSoLinger();
+ }
+
+ @Override public void sendUrgentData(int x) throws IOException
+ {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ @Override public void setOOBInline(boolean on) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setOOBInline(on);
+ else
+ super.setOOBInline(on);
+ }
+
+ @Override public boolean getOOBInline() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getOOBInline();
+ return super.getOOBInline();
+ }
+
+ @Override public void setSoTimeout(int timeout) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setSoTimeout(timeout);
+ else
+ super.setSoTimeout(timeout);
+ }
+
+ @Override public int getSoTimeout() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getSoTimeout();
+ return super.getSoTimeout();
+ }
+
+ @Override public void setSendBufferSize(int size) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setSendBufferSize(size);
+ else
+ super.setSendBufferSize(size);
+ }
+
+ @Override public int getSendBufferSize() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getSendBufferSize();
+ return super.getSendBufferSize();
+ }
+
+ @Override public void setReceiveBufferSize(int size) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setReceiveBufferSize(size);
+ else
+ underlyingSocket.setReceiveBufferSize(size);
+ }
+
+ @Override public int getReceiveBufferSize() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getReceiveBufferSize();
+ return super.getReceiveBufferSize();
+ }
+
+ @Override public void setKeepAlive(boolean on) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setKeepAlive(on);
+ else
+ super.setKeepAlive(on);
+ }
+
+ @Override public boolean getKeepAlive() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getKeepAlive();
+ return super.getKeepAlive();
+ }
+
+ @Override public void setTrafficClass(int tc) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setTrafficClass(tc);
+ else
+ super.setTrafficClass(tc);
+ }
+
+ @Override public int getTrafficClass() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getTrafficClass();
+ return super.getTrafficClass();
+ }
+
+ @Override public void setReuseAddress(boolean reuseAddress)
+ throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setReuseAddress(reuseAddress);
+ else
+ super.setReuseAddress(reuseAddress);
+ }
+
+ @Override public boolean getReuseAddress() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getReuseAddress();
+ return super.getReuseAddress();
+ }
+
+ @Override public void close() throws IOException
+ {
+ // XXX closure alerts.
+ if (underlyingSocket != null && autoClose)
+ underlyingSocket.close();
+ else
+ super.close();
+ }
+
+ @Override public void shutdownInput() throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.shutdownInput();
+ else
+ super.shutdownInput();
+ }
+
+ @Override public void shutdownOutput() throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.shutdownOutput();
+ else
+ super.shutdownOutput();
+ }
+
+ @Override public boolean isConnected()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isConnected();
+ return super.isConnected();
+ }
+
+ @Override public boolean isBound()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isBound();
+ return super.isBound();
+ }
+
+ @Override public boolean isClosed()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isClosed();
+ return super.isClosed();
+ }
+
+ @Override public boolean isInputShutdown()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isInputShutdown();
+ return super.isInputShutdown();
+ }
+
+ @Override public boolean isOutputShutdown()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isOutputShutdown();
+ return super.isOutputShutdown();
+ }
+}