| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.xnet.provider.jsse; |
| |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.lang.reflect.Field; |
| import java.net.InetAddress; |
| import java.net.Socket; |
| import java.net.SocketAddress; |
| import java.net.SocketException; |
| import java.net.UnknownHostException; |
| import java.util.ArrayList; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import javax.net.ssl.HandshakeCompletedEvent; |
| import javax.net.ssl.HandshakeCompletedListener; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.SSLSocket; |
| |
| import org.apache.harmony.luni.platform.INetworkSystem; |
| import org.apache.harmony.luni.platform.Platform; |
| |
| /** |
| * SSLSocket implementation. |
| * @see javax.net.ssl.SSLSocket class documentation for more information. |
| */ |
| public class SSLSocketImpl extends SSLSocket { |
| |
| // indicates if handshake has been started |
| private boolean handshake_started = false; |
| |
| protected static INetworkSystem netImpl = Platform.getNetworkSystem(); |
| |
| // application data input stream, this stream is presented by |
| // ssl socket as an input stream. Additionaly this object is a |
| // place where application data will be stored by record protocol |
| private SSLSocketInputStream appDataIS; |
| // outcoming application data stream |
| private SSLSocketOutputStream appDataOS; |
| // active session object |
| private SSLSessionImpl session; |
| |
| private boolean socket_was_closed = false; |
| |
| // the sslParameters object encapsulates all the info |
| // about supported and enabled cipher suites and protocols, |
| // as well as the information about client/server mode of |
| // ssl socket, whether it require/want client authentication or not, |
| // and controls whether new SSL sessions may be established by this |
| // socket or not. |
| protected SSLParameters sslParameters; |
| // super's streams to be wrapped: |
| protected InputStream input; |
| protected OutputStream output; |
| // handshake complete listeners |
| private ArrayList<HandshakeCompletedListener> listeners; |
| // logger |
| private Logger.Stream logger = Logger.getStream("socket"); |
| |
| // Pointer to the OpenSSL SSL struct used for this connection |
| private long SSL; |
| |
| private static Field fdField; |
| private static Field descriptorField; |
| private FileDescriptor fd; |
| |
| // ----------------- Constructors and initializers -------------------- |
| private static Field getField(final String className, final String fieldName) { |
| return AccessController.doPrivileged(new PrivilegedAction<Field>() { |
| public Field run() { |
| Field field = null; |
| try { |
| Class clazz = Class.forName(className); |
| field = clazz.getDeclaredField(fieldName); |
| field.setAccessible(true); |
| } catch (NoSuchFieldException e) { |
| throw new Error(e); |
| } catch (ClassNotFoundException e) { |
| throw new Error(e); |
| } |
| return field; |
| } |
| }); |
| } |
| |
| { |
| fdField = getField("java.net.SocketImpl", "fd"); |
| descriptorField = getField("java.io.FileDescriptor", "descriptor"); |
| } |
| |
| private native long initImpl(long context); |
| |
| SSLSocketImpl(Socket socket, boolean autoClose, SSLParameters sslParameters) throws IOException { |
| super(); |
| |
| this.sslParameters = sslParameters; |
| init(); |
| } |
| |
| /** |
| * Constructor |
| * @param sslParameters: SSLParameters |
| * @see javax.net.ssl.SSLSocket#SSLSocket() method documentation |
| * for more information. |
| */ |
| protected SSLSocketImpl(SSLParameters sslParameters) throws IOException{ |
| super(); |
| this.sslParameters = sslParameters; |
| // init should be called after creation! |
| // Initialise SSL now so it can be used |
| SSL = initImpl(sslParameters.getSSLContextAddress()); |
| } |
| |
| /** |
| * Constructor |
| * @param host: String |
| * @param port: int |
| * @param sslParameters: SSLParameters |
| * @throws IOException |
| * @throws UnknownHostException |
| * @see javax.net.ssl.SSLSocket#SSLSocket(String,int) |
| * method documentation for more information. |
| */ |
| protected SSLSocketImpl(String host, int port, SSLParameters sslParameters) |
| throws IOException, UnknownHostException { |
| super(host, port); |
| this.sslParameters = sslParameters; |
| init(); |
| } |
| |
| /** |
| * Constructor |
| * @param host: String |
| * @param port: int |
| * @param localHost: InetAddress |
| * @param localPort: int |
| * @param sslParameters: SSLParameters |
| * @throws IOException |
| * @throws UnknownHostException |
| * @see javax.net.ssl.SSLSocket#SSLSocket(String,int,InetAddress,int) |
| * method documentation for more information. |
| */ |
| protected SSLSocketImpl(String host, int port, |
| InetAddress localHost, int localPort, |
| SSLParameters sslParameters) throws IOException, |
| UnknownHostException { |
| super(host, port, localHost, localPort); |
| this.sslParameters = sslParameters; |
| init(); |
| } |
| |
| /** |
| * Constructor |
| * @param host: InetAddress |
| * @param port: int |
| * @param sslParameters: SSLParameters |
| * @return |
| * @throws IOException |
| * @see javax.net.ssl.SSLSocket#SSLSocket(InetAddress,int) |
| * method documentation for more information. |
| */ |
| protected SSLSocketImpl(InetAddress host, int port, |
| SSLParameters sslParameters) throws IOException { |
| super(host, port); |
| this.sslParameters = sslParameters; |
| init(); |
| } |
| |
| /** |
| * Constructor |
| * @param address: InetAddress |
| * @param port: int |
| * @param localAddress: InetAddress |
| * @param localPort: int |
| * @param sslParameters: SSLParameters |
| * @return |
| * @throws IOException |
| * @see javax.net.ssl.SSLSocket#SSLSocket(InetAddress,int,InetAddress,int) |
| * method documentation for more information. |
| */ |
| protected SSLSocketImpl(InetAddress address, int port, |
| InetAddress localAddress, int localPort, |
| SSLParameters sslParameters) throws IOException { |
| super(address, port, localAddress, localPort); |
| this.sslParameters = sslParameters; |
| init(); |
| } |
| |
| /** |
| * Initialize the SSL socket. |
| */ |
| protected void init() throws IOException { |
| if (appDataIS != null) { |
| // already initialized |
| return; |
| } |
| initTransportLayer(); |
| appDataIS = new SSLSocketInputStream(this); |
| appDataOS = new SSLSocketOutputStream(this); |
| |
| if (SSL == 0) { |
| SSL = initImpl(sslParameters.getSSLContextAddress()); |
| } |
| } |
| |
| /** |
| * Initialize the transport data streams. |
| */ |
| protected void initTransportLayer() throws IOException { |
| input = super.getInputStream(); |
| output = super.getOutputStream(); |
| } |
| |
| /** |
| * Closes the transport data streams. |
| */ |
| protected void closeTransportLayer() throws IOException { |
| super.close(); |
| if (input != null) { |
| input.close(); |
| output.close(); |
| } |
| } |
| |
| long getSSL() { |
| return SSL; |
| } |
| |
| // --------------- SSLParameters based methods --------------------- |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites() |
| * method documentation for more information |
| */ |
| @Override |
| public String[] getSupportedCipherSuites() { |
| return sslParameters.getSupportedCipherSuites(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites() |
| * method documentation for more information |
| */ |
| @Override |
| public String[] getEnabledCipherSuites() { |
| return sslParameters.getEnabledCipherSuites(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[]) |
| * method documentation for more information |
| */ |
| @Override |
| public void setEnabledCipherSuites(String[] suites) { |
| sslParameters.setEnabledCipherSuites(SSL, suites); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getSupportedProtocols() |
| * method documentation for more information |
| */ |
| @Override |
| public String[] getSupportedProtocols() { |
| return sslParameters.getSupportedProtocols(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getEnabledProtocols() |
| * method documentation for more information |
| */ |
| @Override |
| public String[] getEnabledProtocols() { |
| return sslParameters.getEnabledProtocols(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#setEnabledProtocols(String[]) |
| * method documentation for more information |
| */ |
| @Override |
| public void setEnabledProtocols(String[] protocols) { |
| sslParameters.setEnabledProtocols(SSL, protocols); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean) |
| * method documentation for more information |
| */ |
| @Override |
| public void setUseClientMode(boolean mode) { |
| if (handshake_started) { |
| throw new IllegalArgumentException( |
| "Could not change the mode after the initial handshake has begun."); |
| } |
| sslParameters.setUseClientMode(mode); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getUseClientMode() |
| * method documentation for more information |
| */ |
| @Override |
| public boolean getUseClientMode() { |
| return sslParameters.getUseClientMode(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean) |
| * method documentation for more information |
| */ |
| @Override |
| public void setNeedClientAuth(boolean need) { |
| sslParameters.setNeedClientAuth(SSL, need); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getNeedClientAuth() |
| * method documentation for more information |
| */ |
| @Override |
| public boolean getNeedClientAuth() { |
| return sslParameters.getNeedClientAuth(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean) |
| * method documentation for more information |
| */ |
| @Override |
| public void setWantClientAuth(boolean want) { |
| sslParameters.setWantClientAuth(SSL, want); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getWantClientAuth() |
| * method documentation for more information |
| */ |
| @Override |
| public boolean getWantClientAuth() { |
| return sslParameters.getWantClientAuth(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean) |
| * method documentation for more information |
| */ |
| @Override |
| public void setEnableSessionCreation(boolean flag) { |
| sslParameters.setEnableSessionCreation(flag); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getEnableSessionCreation() |
| * method documentation for more information |
| */ |
| @Override |
| public boolean getEnableSessionCreation() { |
| return sslParameters.getEnableSessionCreation(); |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getSession() |
| * method documentation for more information |
| */ |
| @Override |
| public SSLSession getSession() { |
| if (!handshake_started) { |
| try { |
| startHandshake(); |
| } catch (IOException e) { |
| // return an invalid session with |
| // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" |
| return SSLSessionImpl.NULL_SESSION; |
| } |
| } |
| return session; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(HandshakeCompletedListener) |
| * method documentation for more information |
| */ |
| @Override |
| public void addHandshakeCompletedListener( |
| HandshakeCompletedListener listener) { |
| if (listener == null) { |
| throw new IllegalArgumentException("Provided listener is null"); |
| } |
| if (listeners == null) { |
| listeners = new ArrayList<HandshakeCompletedListener>(); |
| } |
| listeners.add(listener); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(HandshakeCompletedListener) |
| * method documentation for more information |
| */ |
| @Override |
| public void removeHandshakeCompletedListener( |
| HandshakeCompletedListener listener) { |
| if (listener == null) { |
| throw new IllegalArgumentException("Provided listener is null"); |
| } |
| if (listeners == null) { |
| throw new IllegalArgumentException( |
| "Provided listener is not registered"); |
| } |
| if (!listeners.remove(listener)) { |
| throw new IllegalArgumentException( |
| "Provided listener is not registered"); |
| } |
| } |
| |
| private native void sslConnectImpl(long sslContextAddress, long fd); |
| private native void sslAcceptImpl(long sslContextAddress, long fd); |
| |
| /** |
| * Performs the handshake process over the SSL/TLS connection |
| * as described in rfc 2246, TLS v1 specification |
| * http://www.ietf.org/rfc/rfc2246.txt. If the initial handshake |
| * has been already done, this method initiates rehandshake. |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#startHandshake() |
| * method documentation for more information |
| */ |
| @Override |
| public void startHandshake() throws IOException { |
| if (appDataIS == null) { |
| throw new IOException("Socket is not connected."); |
| } |
| if (socket_was_closed) { |
| throw new IOException("Socket has already been closed."); |
| } |
| |
| // Set the thread local SSLParameter so it can be used in the RNG callbacks |
| SSLParameters.threadLocalParams.set(sslParameters); |
| |
| if (!handshake_started) { |
| handshake_started = true; |
| |
| long descriptor; |
| try { |
| fd = (FileDescriptor) fdField.get(impl); |
| descriptor = descriptorField.getLong(fd); |
| } catch (IllegalAccessException e) { |
| SSLParameters.threadLocalParams.remove(); |
| throw new Error(e); |
| } |
| |
| SSLSessionContextImpl sessionContext; |
| if (sslParameters.getUseClientMode()) { |
| if (logger != null) { |
| logger.println("SSLSocketImpl: CLIENT connecting"); |
| } |
| |
| sslConnectImpl(SSL, descriptor); |
| sessionContext = sslParameters.getClientSessionContext(); |
| } else { |
| if (logger != null) { |
| logger.println("SSLSocketImpl: SERVER accepting connection"); |
| } |
| sslAcceptImpl(SSL, descriptor); |
| sessionContext = sslParameters.getServerSessionContext(); |
| } |
| |
| session = new SSLSessionImpl(this, sslParameters, SSL); |
| sessionContext.putSession(session); |
| } |
| |
| if (logger != null) { |
| logger.println("SSLSocketImpl: Handshake complete, notifying listeners"); |
| } |
| |
| // Notify handshake completion listeners |
| if (listeners != null) { |
| HandshakeCompletedEvent event = |
| new HandshakeCompletedEvent(this, session); |
| int size = listeners.size(); |
| for (int i=0; i<size; i++) { |
| listeners.get(i) |
| .handshakeCompleted(event); |
| } |
| } |
| |
| if (logger != null) { |
| logger.println("SSLSocketImpl.startHandshake: END"); |
| } |
| |
| // Remove our thread local SSLParameter now we are complete |
| SSLParameters.threadLocalParams.remove(); |
| } |
| |
| // ---------------- Socket's methods overridings ------------------- |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getInputStream() |
| * method documentation for more information |
| */ |
| @Override |
| public InputStream getInputStream() throws IOException { |
| if (socket_was_closed) { |
| throw new IOException("Socket has already been closed."); |
| } |
| return appDataIS; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#getOutputStream() |
| * method documentation for more information |
| */ |
| @Override |
| public OutputStream getOutputStream() throws IOException { |
| if (socket_was_closed) { |
| throw new IOException("Socket has already been closed."); |
| } |
| return appDataOS; |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see java.net.Socket#connect(SocketAddress) |
| * method documentation for more information |
| */ |
| @Override |
| public void connect(SocketAddress endpoint) throws IOException { |
| super.connect(endpoint); |
| init(); |
| } |
| |
| /** |
| * This method works according to the specification of implemented class. |
| * @see java.net.Socket#connect(SocketAddress,int) |
| * method documentation for more information |
| */ |
| @Override |
| public void connect(SocketAddress endpoint, int timeout) |
| throws IOException { |
| super.connect(endpoint, timeout); |
| init(); |
| } |
| |
| |
| private static native void closeImpl(long ssl); |
| /** |
| * This method works according to the specification of implemented class. |
| * @see javax.net.ssl.SSLSocket#close() |
| * method documentation for more information |
| */ |
| @Override |
| public void close() throws IOException { |
| if (logger != null) { |
| logger.println("SSLSocket.close "+socket_was_closed); |
| } |
| // TODO: Call down into natives to close down OpenSSL connection an clean up structs |
| if (!socket_was_closed) { |
| if (SSL != 0) { |
| closeImpl(SSL); |
| } |
| closeTransportLayer(); |
| socket_was_closed = true; |
| SSL = 0; |
| } |
| } |
| |
| /** |
| * This method is not supported for SSLSocket implementation. |
| */ |
| @Override |
| public void sendUrgentData(int data) throws IOException { |
| throw new SocketException( |
| "Method sendUrgentData() is not supported."); |
| } |
| |
| /** |
| * This method is not supported for SSLSocket implementation. |
| */ |
| @Override |
| public void setOOBInline(boolean on) throws SocketException { |
| throw new SocketException( |
| "Methods sendUrgentData, setOOBInline are not supported."); |
| } |
| |
| /** |
| * This method is not supported for SSLSocket implementation. |
| */ |
| @Override |
| public void shutdownOutput() { |
| throw new UnsupportedOperationException( |
| "Method shutdownOutput() is not supported."); |
| } |
| |
| /** |
| * This method is not supported for SSLSocket implementation. |
| */ |
| @Override |
| public void shutdownInput() { |
| throw new UnsupportedOperationException( |
| "Method shutdownInput() is not supported."); |
| } |
| |
| /** |
| * Returns the string representation of the object. |
| */ |
| @Override |
| public String toString() { |
| return "[SSLSocketImpl]"; |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| private native int readAppDataImpl(long ssl, byte[] data, int off, int len); |
| |
| protected int readAppData(byte[] data, int off, int len) throws IOException { |
| if (!handshake_started) { |
| startHandshake(); |
| } |
| int type; |
| if (logger != null) { |
| logger.println("SSLSocket.needAppData.."); |
| } |
| |
| return readAppDataImpl(SSL, data, off, len); |
| } |
| |
| protected int available() throws IOException { |
| return netImpl.availableStream(fd); |
| } |
| |
| private native void writeAppDataImpl(long SSL, byte[] data, int offset, int len); |
| /** |
| * This method is called by SSLSocketOutputStream when client application |
| * tryes to send the data over ssl protocol. |
| */ |
| protected void writeAppData(byte[] data, int offset, int len) |
| throws IOException { |
| if (!handshake_started) { |
| startHandshake(); |
| } |
| if (logger != null) { |
| logger.println("SSLSocket.writeAppData: " + len); |
| //logger.println(new String(data, offset, len)); |
| } |
| |
| writeAppDataImpl(SSL, data, offset, len); |
| } |
| } |
| |