/* * 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 net.jini.jeri.ssl; import com.sun.jini.jeri.internal.http.ConnectionTimer; import com.sun.jini.jeri.internal.http.HttpServerConnection; import com.sun.jini.jeri.internal.http.HttpServerManager; import com.sun.jini.jeri.internal.http.HttpSettings; import com.sun.jini.logging.Levels; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.security.AccessControlContext; import java.security.Principal; import java.security.PrivilegedAction; import java.security.SecureRandom; import java.security.cert.CertPath; import java.security.cert.CertificateFactory; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500PrivateCredential; import net.jini.core.constraint.ClientAuthentication; import net.jini.core.constraint.ClientMaxPrincipal; import net.jini.core.constraint.ClientMaxPrincipalType; import net.jini.core.constraint.ClientMinPrincipal; import net.jini.core.constraint.ClientMinPrincipalType; import net.jini.core.constraint.Confidentiality; import net.jini.core.constraint.ConnectionAbsoluteTime; import net.jini.core.constraint.ConnectionRelativeTime; import net.jini.core.constraint.ConstraintAlternatives; import net.jini.core.constraint.Delegation; import net.jini.core.constraint.DelegationAbsoluteTime; import net.jini.core.constraint.DelegationRelativeTime; import net.jini.core.constraint.Integrity; import net.jini.core.constraint.InvocationConstraints; import net.jini.core.constraint.ServerAuthentication; import net.jini.core.constraint.ServerMinPrincipal; import net.jini.io.UnsupportedConstraintException; import net.jini.io.context.AcknowledgmentSource; import net.jini.io.context.ClientHost; import net.jini.io.context.ClientSubject; import net.jini.jeri.BasicJeriExporter; import net.jini.jeri.Endpoint; import net.jini.jeri.InboundRequest; import net.jini.jeri.RequestDispatcher; import net.jini.jeri.ServerEndpoint.ListenEndpoint; import net.jini.jeri.ServerEndpoint.ListenHandle; import net.jini.jeri.ServerEndpoint; import net.jini.jeri.connection.InboundRequestHandle; import net.jini.jeri.connection.ServerConnection; import net.jini.jeri.connection.ServerConnectionManager; import net.jini.security.AuthenticationPermission; import net.jini.security.SecurityContext; /** * An implementation of {@link ServerEndpoint} that uses HTTPS (HTTP over * TLS/SSL) to support invocation constraints for communication through * firewalls. <p> * * Instances of this class are intended to be created for use with the {@link * BasicJeriExporter} class. Calls to {@link #enumerateListenEndpoints * enumerateListenEndpoints} return instances of {@link HttpsEndpoint}. <p> * * This class supports at least the following constraints, possibly limited by * the available cipher suites: <p> * * <ul> * <li> {@link ClientAuthentication} * <li> {@link ClientMaxPrincipal}, when it contains an {@link X500Principal} * <li> {@link ClientMaxPrincipalType}, when it contains * <code>X500Principal</code> * <li> {@link ClientMinPrincipal}, when it contains a single * <code>X500Principal</code> only * <li> {@link ClientMinPrincipalType}, when it contains * <code>X500Principal</code> only * <li> {@link Confidentiality} * <li> {@link ConfidentialityStrength}, a provider-specific constraint for * specifying weak or strong confidentiality * <li> {@link ConnectionAbsoluteTime}, trivially, since this only takes effect * on the client side * <li> {@link ConnectionRelativeTime}, trivially, since this only takes effect * on the client side * <li> {@link ConstraintAlternatives}, if the elements all have the same * actual class and at least one element is supported * <li> {@link Delegation#NO} * <li> {@link Delegation#YES}, trivially, for anonymous clients * <li> {@link DelegationAbsoluteTime}, trivially, when delegation is not * supported * <li> {@link DelegationRelativeTime}, trivially, when delegation is not * supported * <li> {@link Integrity#YES} * <li> {@link ServerAuthentication} * <li> {@link ServerMinPrincipal}, when it contains a single * <code>X500Principal</code> only * </ul> <p> * * This class authenticates as a single {@link Principal} if the following * items are present in the server {@link Subject}: <p> * * <ul> * <li> One or more principals of type <code>X500Principal</code> * <li> For each principal, one or more certificate chains, stored as public * credentials, and represented by instances of {@link CertPath}, whose * <code>getType</code> method returns "X.509", and for which calling * <code>getSubjectDN</code> on the certificate chain's first element * returns that principal's name * <li> For each certificate chain, an instance of {@link * X500PrivateCredential}, stored as a private credential, whose * <code>getCertificate</code> method returns a value equal to the first * element of the certificate chain, and whose <code>getPrivateKey</code> * method returns the associated private key * </ul> <p> * * In addition, this class will only dispatch remote calls that authenticate as * a given principal if the caller of {@link * net.jini.jeri.ServerEndpoint.ListenEndpoint#listen listen} on the class's * {@link net.jini.jeri.ServerEndpoint.ListenEndpoint} has been granted {@link * AuthenticationPermission} with that principal as the local principal, the * principal representing the authenticated identity of the client for the call * (if any) as the peer principal, and the <code>accept</code> action. <p> * * This class supports remote connections between authenticated servers and * authenticated or anonymous clients, and between anonymous servers and * anonymous clients. Connections between anonymous servers and authenticated * clients are not supported. Because of the suites available in the TLS/SSL * protocol, support for {@link Confidentiality#NO} requires the server to * authenticate with an RSA public key. <p> * * If the server subject contains principals and credentials that would permit * authentication of more than one <code>X500Principal</code>, the endpoint * will make an arbitrary choice of the principal to use for authentication, * and will continue to make the same choice so long as subject contents, * validity of credentials, and security permissions do not change. <p> * * The host name specified when creating an <code>HttpsServerEndpoint</code> * instance controls the host name that will be contained in * <code>HttpsEndpoint</code> instances produced when {@link * #enumerateListenEndpoints enumerateListenEndpoints} is invoked to listen on * the server endpoint; the host name does not affect the behavior of the * listen operation itself, which listens on all of the local system's network * addresses. If the host name in the server endpoint is <code>null</code>, * then the host name in the <code>HttpsEndpoint</code> instances that it * produces will be the default server host name, which is the IP address * string of the {@link InetAddress} returned by {@link * InetAddress#getLocalHost InetAddress.getLocalHost} when * <code>enumerateListenEndpoints</code> is invoked. <p> * * This class permits specifying a {@link SocketFactory} for creating the * {@link Socket} instances that the associated <code>HttpsEndpoint</code> * instances use to make remote connections back to the server, and a {@link * ServerSocketFactory} for creating the {@link ServerSocket} instances that * the server endpoint uses to accept remote connections. These socket * factories and sockets should not implement the TLS/SSL protocol; it is the * responsibility of the implementation to establish TLS/SSL connections over * the sockets it obtains from the socket factories. In particular, instances * of {@link SSLSocketFactory} and {@link SSLServerSocketFactory} should not be * used, and the factories used should not return instances of {@link * SSLSocket} or {@link SSLServerSocket}. <p> * * A <code>SocketFactory</code> used with instances of this class should be * serializable, and must implement {@link Object#equals Object.equals} to obey * the guidelines that are specified for <code>equals</code> methods of {@link * Endpoint} instances. A <code>ServerSocketFactory</code> used with instances * of this class must implement <code>Object.equals</code> to obey the * guidelines that are specified for <code>equals</code> methods of {@link * net.jini.jeri.ServerEndpoint.ListenEndpoint ListenEndpoint} instances. * * @author Sun Microsystems, Inc. * @see HttpsEndpoint * @see ConfidentialityStrength * @since 2.0 * * @com.sun.jini.impl <!-- Implementation Specifics --> * * This implementation uses the <a * href="http://java.sun.com/j2se/1.4/docs/guide/security/jsse/JSSERefGuide.html" * target="_top">Java(TM) Secure Socket Extension (JSSE)</a>. <p> * * This implementation uses the {@link ServerConnectionManager} class to manage * connections. <p> * * This implementation uses the following {@link Logger} instances in the * <code>net.jini.jeri.ssl</code> namespace: <p> * * <ul> * <li> <a href="#init_logger">init</a> - problems during initialization * <li> <a href="#server_logger">server</a> - information about * server-side connections * </ul> <p> * * <a name="init_logger"></a> * <table border="1" cellpadding="5" summary="Describes logging to the init * logger performed by the HttpsServerEndpoint class at different * logging levels"> * * <caption halign="center" valign="top"><b><code> * net.jini.jeri.ssl.init</code></b></caption> * * <tr> <th scope="col"> Level <th scope="col"> Description * * <tr> <td> {@link Level#WARNING WARNING} <td> problems with initializing JSSE * * </table> <p> * * <a name="server_logger"></a> * <table border="1" cellpadding="5" summary="Describes logging to the server * logger performed by the HttpsServerEndpoint class at different * logging levels"> * * <caption halign="center" valign="top"><b><code> * net.jini.jeri.ssl.server</code></b></caption> * * <tr> <th scope="col"> Level <th scope="col"> Description * * <tr> <td> {@link Level#INFO INFO} <td> problems with accepting or handling * server connections, or with handling inbound requests * * <tr> <td> {@link Levels#FAILED FAILED} <td> problems with checking * constraints or permissions, with enumerating listen endpoints, or with * security issues for inbound requests * * <tr> <td> {@link Levels#HANDLED HANDLED} <td> exceptions caught involving * authentication * * <tr> <td> {@link Level#FINE FINE} <td> creating server endpoints, * enumerating listen endpoints, creating or closing connections or listen * handles, or checking constraints for endpoints or inbound requests * * <tr> <td> {@link Level#FINEST FINEST} <td> low level operation tracing * * </table> <p> * * This implementation uses the following security providers: <p> * * <ul> * <li> {@link SSLContext}, with the protocol specified by the * <code>com.sun.jini.jeri.ssl.sslProtocol</code> system property, or * <code>"TLS"</code> if that property is not defined, to provide the * TLS/SSL implementation. The {@link SSLContext#init SSLContext.init} * method is called with <code>null</code> for the <code>random</code> * parameter to use the default {@link SecureRandom} implementation. * <li> {@link CertificateFactory}, with type <code>"X.509"</code>, to generate * <code>CertPath</code> instances from X.509 certificate chains * <li> {@link TrustManagerFactory}, with the algorithm specified by the * <code>com.sun.jini.jeri.ssl.trustManagerFactoryAlgorithm</code> system * property, or the default algorithm if that property is not defined, to * implement trust management for the TLS/SSL implementation. The factory * must return trust managers that implement {@link X509TrustManager}. * </ul> <p> * * See the documentation on <a * href="http://java.sun.com/j2se/1.4/docs/guide/security/CryptoSpec.html#ProviderInstalling" * target="_top">installing security providers</a> and <a * href="http://java.sun.com/j2se/1.4/docs/guide/security/jsse/JSSERefGuide.html#ProviderCust" * target="_top">configuring JSSE</a> for information on configuring these * providers. <p> * * The <a * href="http://java.sun.com/j2se/1.4/docs/guide/security/jsse/JSSERefGuide.html#Customization" * target="_top">JSSE documentation</a> also describes the system properties * for configuring the location, type, and password of the truststore that this * implementation uses, through JSSE, to make decisions about what certificate * chains should be trusted. <p> * * This implementation recognizes the following system properties: <p> * * <ul> * <li> <code>com.sun.jini.jeri.ssl.maxServerSessionDuration</code> - The * maximum number of milliseconds a server-side TLS/SSL session should be * used before expiring. The default is 24 hours. The value used should be * larger than the maximum client session duration to allow the client to * negotiate a new session before the server timeout occurs. * <li> <code>com.sun.jini.jeri.ssl.sslProtocol</code> - The secure socket * protocol used when obtaining {@link SSLContext} instances. The default * is <code>"TLS"</code>. * <li> <code>com.sun.jini.jeri.ssl.trustManagerFactoryAlgorithm</code> - The * algorithm used when obtaining {@link TrustManagerFactory} * instances. The default is the value returned by {@link * TrustManagerFactory#getDefaultAlgorithm * TrustManagerFactory.getDefaultAlgorithm}. * <li> <code>com.sun.jini.jeri.ssl.cipherSuites</code> - The TLS/SSL cipher * suites that should be used for communication. The default is the list * of suites supported by the JSSE implementation. The value should * specify the suite names, separated by commas. The value will be ignored * if it contains no suites or specifies suites that are not supported by * the JSSE implementation. Suites appearing earlier in the list will be * preferred to ones appearing later for suites that support the same * requirements and preferences. * <li> <code>com.sun.jini.jeri.https.idleServerConnectionTimeout</code> - The * number of milliseconds to retain idle server-side HTTPS connections * before closing them. The default is the idle client-side connection * timeout (as specified by the * <code>com.sun.jini.jeri.https.idleConnectionTimeout</code> system * property) plus <code>30000</code>. * <li> <code>com.sun.jini.jeri.https.responseAckTimeout</code> - The number of * milliseconds to wait for acknowledgments from {@link * AcknowledgmentSource} instances. The default is <code>15000</code>. * </ul> */ public final class HttpsServerEndpoint implements ServerEndpoint { /* -- Fields -- */ /** Server logger */ static final Logger logger = Utilities.serverLogger; /** Manager for HTTP server connections. */ static final HttpServerManager httpServerManager; /** Times out idle connections. */ static final ConnectionTimer connectionTimer; static { HttpSettings hs = HttpsEndpoint.getHttpSettings(); httpServerManager = new HttpServerManager(hs.getResponseAckTimeout()); connectionTimer = new ConnectionTimer(hs.getServerConnectionTimeout()); } /** Implementation delegate. */ private final HttpsServerEndpointImpl impl; /* -- Methods -- */ /** * Returns an HTTPS server endpoint for the specified port. Uses the * <code>null</code> server host (which requests that {@link * #enumerateListenEndpoints enumerateListenEndpoints} compute the default * server host), the subject associated with the current access control * context, the principals in the subject with appropriate public and * private credentials for which the caller has {@link * AuthenticationPermission} to listen, and <code>null</code> socket * factories to create default sockets. A <code>port</code> of * <code>0</code> requests listening on any free port. * * @param port the port on which to listen for connections, or * <code>0</code> for any free port * @return an <code>HttpsServerEndpoint</code> instance * @throws IllegalArgumentException if <code>port</code> is negative or * greater than <code>65535</code> */ public static HttpsServerEndpoint getInstance(int port) { return new HttpsServerEndpoint(null, null, null, port, null, null); } /** * Returns an HTTPS server endpoint for the specified server host and port. * Uses the subject associated with the current access control context, the * principals in the subject with appropriate public and private * credentials for which the caller has {@link AuthenticationPermission} to * listen, and <code>null</code> socket factories to create default * sockets. A <code>serverHost</code> of <code>null</code> requests that * {@link #enumerateListenEndpoints enumerateListenEndpoints} compute the * default server host. A <code>port</code> of <code>0</code> requests * listening on any free port. * * @param serverHost the name that clients should use to connect to this * server, or <code>null</code> to use the default * @param port the port on which to listen for connections, or * <code>0</code> for any free port * @return an <code>HttpsServerEndpoint</code> instance * @throws IllegalArgumentException if <code>port</code> is negative or * greater than <code>65535</code> */ public static HttpsServerEndpoint getInstance(String serverHost, int port) { return new HttpsServerEndpoint( null, null, serverHost, port, null, null); } /** * Returns an HTTPS server endpoint for the specified server host, port, * and socket factories. Uses the subject associated with the current * access control context, and the principals in the subject with * appropriate public and private credentials for which the caller has * {@link AuthenticationPermission} to listen. A <code>serverHost</code> of * <code>null</code> requests that {@link #enumerateListenEndpoints * enumerateListenEndpoints} compute the default server host. A * <code>port</code> of <code>0</code> requests listening on any free * port. A <code>socketFactory</code> of <code>null</code> uses default * sockets in the associated {@link HttpsEndpoint}. A * <code>serverSocketFactory</code> of <code>null</code> uses default * server sockets. * * @param serverHost the name that clients should use to connect to this * server, or <code>null</code> to use the default * @param port the port on which to listen for connections, or * <code>0</code> for any free port * @param socketFactory the socket factory for use in the associated * <code>HttpsEndpoint</code> instances, or <code>null</code> * @param serverSocketFactory the server socket factory, or * <code>null</code> * @return an <code>HttpsServerEndpoint</code> instance * @throws IllegalArgumentException if <code>port</code> is negative or * greater than <code>65535</code> */ public static HttpsServerEndpoint getInstance( String serverHost, int port, SocketFactory socketFactory, ServerSocketFactory serverSocketFactory) { return new HttpsServerEndpoint(null, null, serverHost, port, socketFactory, serverSocketFactory); } /** * Returns an HTTPS server endpoint for the specified server subject, * server principals, server host, and port. Uses <code>null</code> socket * factories to create default sockets. A <code>serverSubject</code> of * <code>null</code> uses the subject associated with the current access * control context. A <code>serverPrincipals</code> of <code>null</code> * uses the principals in the subject with appropriate public and private * credentials for which the caller has {@link AuthenticationPermission} to * listen; otherwise that argument specifies the principals to use, or that * the server should be anonymous if the argument has no elements. If * non-<code>null</code>, the value of <code>serverPrincipals</code> is * neither retained nor modified; subsequent changes to that argument have * no effect on the instance created. A <code>serverHost</code> of * <code>null</code> requests that {@link #enumerateListenEndpoints * enumerateListenEndpoints} compute the default server host. A * <code>port</code> of <code>0</code> requests listening on any free port. * * @param serverSubject the <code>Subject</code> to use for authenticating * the server or <code>null</code> to use the current subject * @param serverPrincipals the principals to use for authenticating the * server, or <code>null</code> to use any available principals in * the subject * @param serverHost the name that clients should use to connect to this * server, or <code>null</code> to use the default * @param port the port on which to listen for connections, or * <code>0</code> for any free port * @return an <code>HttpsServerEndpoint</code> instance * @throws IllegalArgumentException if <code>port</code> is negative or * greater than <code>65535</code> * @throws NullPointerException if <code>serverPrincipals</code> is not * <code>null</code> and any of its elements are <code>null</code> */ public static HttpsServerEndpoint getInstance( Subject serverSubject, X500Principal[] serverPrincipals, String serverHost, int port) { return new HttpsServerEndpoint(serverSubject, serverPrincipals, serverHost, port, null, null); } /** * Returns an HTTPS server endpoint for the specified server subject, * server principals, server host, port, and socket factories. A * <code>serverSubject</code> of <code>null</code> uses the subject * associated with the current access control context. A * <code>serverPrincipals</code> of <code>null</code> uses the principals * in the subject with appropriate public and private credentials for which * the caller has {@link AuthenticationPermission} to listen; otherwise * that argument specifies the principals to use, or that the server should * be anonymous if the argument has no elements. If non-<code>null</code>, * the value of <code>serverPrincipals</code> is neither retained nor * modified; subsequent changes to that argument have no effect on the * instance created. A <code>serverHost</code> of <code>null</code> * requests that {@link #enumerateListenEndpoints enumerateListenEndpoints} * compute the default server host. A <code>port</code> of <code>0</code> * requests listening on any free port. A <code>socketFactory</code> of * <code>null</code> uses default sockets in the associated {@link * HttpsEndpoint}. A <code>serverSocketFactory</code> of <code>null</code> * uses default server sockets. * * @param serverSubject the <code>Subject</code> to use for authenticating * the server or <code>null</code> to use the current subject * @param serverPrincipals the principals to use for authenticating the * server, or <code>null</code> to use any available principals in * the subject * @param serverHost the name that clients should use to connect to this * server, or <code>null</code> to use the default * @param port the port on which to listen for connections, or * <code>0</code> for any free port * @param socketFactory the socket factory for use in the associated * <code>HttpsEndpoint</code> instances, or <code>null</code> * @param serverSocketFactory the server socket factory, or * <code>null</code> * @return an <code>HttpsServerEndpoint</code> instance * @throws IllegalArgumentException if <code>port</code> is negative or * greater than <code>65535</code> * @throws NullPointerException if <code>serverPrincipals</code> is not * <code>null</code> and any of its elements are <code>null</code> */ public static HttpsServerEndpoint getInstance( Subject serverSubject, X500Principal[] serverPrincipals, String serverHost, int port, SocketFactory socketFactory, ServerSocketFactory serverSocketFactory) { return new HttpsServerEndpoint(serverSubject, serverPrincipals, serverHost, port, socketFactory, serverSocketFactory); } /** Creates an instance of this class. */ private HttpsServerEndpoint(Subject serverSubject, X500Principal[] serverPrincipals, String serverHost, int port, SocketFactory socketFactory, ServerSocketFactory serverSocketFactory) { impl = new HttpsServerEndpointImpl( this, serverSubject, serverPrincipals, serverHost, port, socketFactory, serverSocketFactory); logger.log(Level.FINE, "created {0}", this); } /** * Returns the host name that will be used in {@link HttpsEndpoint} * instances created by listening on this object, or <code>null</code> if * {@link #enumerateListenEndpoints enumerateListenEndpoints} will * use the default server host. * * @return the host name to use in <code>HttpsEndpoint</code> instances * created by listening on this object, or <code>null</code> if * using the default */ public String getHost() { return impl.serverHost; } /** * Returns the TCP port on which this object listens for connections, or * <code>0</code> if it selects a free port. * * @return the TCP port on which this object listens for connections, or * <code>0</code> if it selects a free port */ public int getPort() { return impl.port; } /** * Returns an immutable set of the principals that this instance uses for * authentication, or <code>null</code> if it is anonymous. * * @return an immutable set of the principals or <code>null</code> */ public Set getPrincipals() { return impl.serverPrincipals == null ? null : Collections.unmodifiableSet(impl.serverPrincipals); } /** * Returns the socket factory that the associated {@link HttpsEndpoint} * instances created by listening on this server endpoint use to create * {@link Socket} instances, or <code>null</code> if they use default * sockets. * * @return the socket factory that associated endpoints use to create * sockets, or <code>null</code> if they use default sockets */ public SocketFactory getSocketFactory() { return impl.socketFactory; } /** * Returns the server socket factory that this server endpoint uses to * create {@link ServerSocket} instances, or <code>null</code> if it uses * default server sockets. * * @return the server socket factory that this server endpoint uses to * create server sockets, or <code>null</code> if it uses default * server sockets */ public ServerSocketFactory getServerSocketFactory() { return impl.serverSocketFactory; } /** Returns a string representation of this object. */ public String toString() { return "HttpsServerEndpoint" + impl.fieldsToString(); } /* -- Implement ServerCapabilities -- */ /** * Checks that it is possible to receive requests that either * fully or partially satisfy the specified requirements, and * returns any constraints that must be fully or partially * implemented by higher layers in order to fully satisfy all of * the specified requirements. <p> * * This implementation only returns {@link Integrity#YES} constraints. * * @throws SecurityException if the current security context does not have * the permissions necessary to perform this operation * @throws NullPointerException if <code>constraints</code> is * <code>null</code> */ public InvocationConstraints checkConstraints( InvocationConstraints constraints) throws UnsupportedConstraintException { return impl.checkConstraints(constraints); } /* -- Implement ServerEndpoint -- */ /** Returns a hash code value for this object. */ public int hashCode() { return impl.hashCode(); } /** * Two instances of this class are equal if they have server subjects that * compare equal using <code>==</code>; have server principals that are * either both <code>null</code> or are equal when compared as the elements * of a {@link Set}; have the same values for server host and port; have * socket factories that are either both <code>null</code>, or have the * same actual class and are equal; and have server socket factories that * are either both <code>null</code>, or have the same actual class and are * equal. */ public boolean equals(Object object) { if (this == object) { return true; } else { return object instanceof HttpsServerEndpoint && impl.equals(((HttpsServerEndpoint) object).impl); } } /** * Passes the {@link net.jini.jeri.ServerEndpoint.ListenEndpoint * ListenEndpoint} for this <code>HttpsServerEndpoint</code> to * <code>listenContext</code>, which will ensure an active listen * operation on the endpoint, and returns an <code>HttpsEndpoint</code> * instance corresponding to the listen operation chosen by * <code>listenContext</code>. <p> * * If this server endpoint's server host is <code>null</code>, then the * endpoint returned will contain the default server host. This method * computes the default by invoking {@link InetAddress#getLocalHost * InetAddress.getLocalHost} to obtain an <code>InetAddress</code> for the * local host. If <code>InetAddress.getLocalHost</code> throws an * exception, this method throws that exception. The default host name will * be the string returned by invoking {@link InetAddress#getHostAddress * getHostAddress} on that <code>InetAddress</code>. If there is a security * manager, its {@link SecurityManager#checkConnect(String,int) * checkConnect} method will be invoked with the string returned by * invoking {@link InetAddress#getHostName getHostName} on that same * <code>InetAddress</code> as the host argument and <code>-1</code> as the * port argument; this could result in a * <code>SecurityException</code>. <p> * * This method invokes <code>addListenEndpoint</code> on * <code>listenContext</code> once, passing a <code>ListenEndpoint</code> * as described below. If <code>addListenEndpoint</code> throws an * exception, then this method throws that exception. Otherwise, this * method returns an <code>HttpsEndpoint</code> instance with the host name * described above, the TCP port number bound by the listen operation * represented by the {@link net.jini.jeri.ServerEndpoint.ListenHandle * ListenHandle} returned by <code>addListenEndpoint</code>, and the same * <code>SocketFactory</code> as this <code>HttpsServerEndpoint</code>. <p> * * The <code>ListenEndpoint</code> passed to * <code>addListenEndpoint</code> represents the server subject, server * principals, TCP port number, and <code>ServerSocketFactory</code> of * this <code>HttpsServerEndpoint</code>. Its methods behave as follows: * <p> * * {@link net.jini.jeri.ServerEndpoint.ListenEndpoint#listen ListenHandle * listen(RequestDispatcher)}: * * <blockquote> * * Listens for requests received on this endpoint's TCP port, dispatching * them to the supplied <code>RequestDispatcher</code> in the form of * {@link InboundRequest} instances. <p> * * When the implementation of this method needs to create a new * <code>ServerSocket</code>, it will do so by invoking one of the * <code>createServerSocket</code> methods that returns a bound server * socket on the contained <code>ServerSocketFactory</code> if * non-<code>null</code>, or it will create a <code>ServerSocket</code> * directly otherwise. <p> * * If there is a security manager, its {@link SecurityManager#checkListen * checkListen} method will be invoked with this endpoint's TCP port; this * could result in a <code>SecurityException</code>. In addition, for each * server principal in this endpoint, the security manager's {@link * SecurityManager#checkPermission checkPermission} method will be invoked * with an {@link AuthenticationPermission} containing the server * principal and the <code>listen</code> action; this could also result in * a <code>SecurityException</code>. Furthermore, before a given * <code>InboundRequest</code> gets dispatched to the supplied request * dispatcher, the security manager's {@link SecurityManager#checkAccept * checkAccept} method must have been successfully invoked in the security * context of this <code>listen</code> invocation with the remote IP * address and port of the <code>Socket</code> used to receive the * request, and if the server authenticated itself to the client, the * security manager's <code>checkPermission</code> method must have been * successfully invoked in the same context with an * <code>AuthenticationPermission</code> containing that authenticated * server principal as local principal, the client's authenticated * principal (if any) as peer principal, and the <code>accept</code> * action. The <code>checkPermissions</code> method of the dispatched * <code>InboundRequest</code> also performs these latter security checks. * (Note that in some cases, the implementation may carry out some of * these security checks indirectly, such as through invocations of * <code>ServerSocket</code>'s constructors or <code>accept</code> * method.) <p> * * Requests will be dispatched in a {@link PrivilegedAction} wrapped by a * {@link SecurityContext} obtained when this method was invoked, with the * {@link AccessControlContext} of that <code>SecurityContext</code> in * effect. <p> * * Dispatched requests will implement {@link * InboundRequest#populateContext populateContext} to populate the given * collection with an element that implements the {@link ClientHost} * interface, and an element that implements the {@link ClientSubject} * interface. The <code>ClientHost</code> element implements {@link * ClientHost#getClientHost getClientHost} to return the IP address of the * <code>Socket</code> that the request was received over (see {@link * Socket#getInetAddress}). <p> * * Throws {@link IOException} if an I/O exception occurs while performing * this operation, such as if the TCP port is already in use. <p> * * Throws {@link SecurityException} if there is a security manager and an * invocation of its <code>checkListen</code> or * <code>checkPermission</code> method fails. <p> * * Throws {@link NullPointerException} if <code>requestDispatcher</code> * is <code>null</code> * * </blockquote> * * {@link net.jini.jeri.ServerEndpoint.ListenEndpoint#checkPermissions * void checkPermissions()}: * * <blockquote> * * Verifies that the current security context has all of the security * permissions necessary to listen for requests on this endpoint. <p> * * If there is a security manager, its <code>checkListen</code> method * will be invoked with this endpoint's TCP port; this could result in a * <code>SecurityException</code>. In addition, for each server principal * in this endpoint, the security manager's {@link * SecurityManager#checkPermission checkPermission} method will be invoked * with an {@link AuthenticationPermission} containing the server * principal and the <code>listen</code> action; this could also result in * a <code>SecurityException</code>. <p> * * Throws {@link SecurityException} if there is a security manager and an * invocation of its <code>checkListen</code> or * <code>checkPermission</code> method fails. * * </blockquote> * * {@link Object#equals boolean equals(Object)}: * * <blockquote> * * Compares the specified object with this <code>ListenEndpoint</code> for * equality. <p> * * This method returns <code>true</code> if and only if the specified * object is also a <code>ListenEndpoint</code> produced by an * <code>HttpsServerEndpoint</code>, and the two listen endpoints both have * server subjects that compare equal using <code>==</code>; have server * principals that are either both <code>null</code> or are equal when * compared as the elements of a {@link Set}; have the same values for TCP * port; and have server socket factories that are either both * <code>null</code>, or have the same actual class and are equal. * * </blockquote> * * @throws SecurityException if there is a security manager, and either its * {@link SecurityManager#checkListen checkListen} method fails, * or <code>serverHost</code> is <code>null</code> and the security * manager's {@link SecurityManager#checkConnect checkConnect} * method fails; or if the calling thread does not have permission * to authenticate as each of the endpoint's server principals when * listening for connections * @throws IllegalArgumentException if an invocation of the * <code>addListenEndpoint</code> method on the supplied * <code>ListenContext</code> returns a <code>ListenCookie</code> * that does not correspond to the <code>ListenEndpoint</code> that * was passed to it * @throws NullPointerException if <code>listenContext</code> is * <code>null</code> * @throws UnknownHostException if this instance's server host * is <code>null</code> and <code>InetAddress.getLocalHost</code> * throws an <code>UnknownHostException</code> * @throws UnsupportedConstraintException if the server subject is missing * any of the endpoint's server principals or the associated * credentials */ public Endpoint enumerateListenEndpoints(ListenContext listenContext) throws IOException { return impl.enumerateListenEndpoints(listenContext); } /** Implementation delegate. */ private static final class HttpsServerEndpointImpl extends SslServerEndpointImpl { HttpsServerEndpointImpl(ServerEndpoint serverEndpoint, Subject serverSubject, X500Principal[] serverPrincipals, String serverHost, int port, SocketFactory socketFactory, ServerSocketFactory serverSocketFactory) { super(serverEndpoint, serverSubject, serverPrincipals, serverHost, port, socketFactory, serverSocketFactory); } ListenEndpoint createListenEndpoint() { return new HttpsListenEndpoint(); } Endpoint createEndpoint(String serverHost, SslListenCookie cookie) { return HttpsEndpoint.getInstance( serverHost, cookie.getPort(), socketFactory); } /** Implements ListenEndpoint */ private final class HttpsListenEndpoint extends SslListenEndpoint { ListenHandle createListenHandle(RequestDispatcher requestDispatcher, ServerSocket serverSocket) throws IOException { return new HttpsListenHandle(requestDispatcher, serverSocket); } } /** Implements ListenHandle */ private final class HttpsListenHandle extends SslListenHandle { HttpsListenHandle(RequestDispatcher requestDispatcher, ServerSocket serverSocket) throws IOException { super(requestDispatcher, serverSocket); } SslServerConnection serverConnection(Socket socket) throws IOException { return new HttpsServerConnection(this, socket); } void handleConnection(SslServerConnection connection, RequestDispatcher requestDispatcher) { try { new HttpServer( connection.sslSocket, new HttpsRequestDispatcher( requestDispatcher, (HttpsServerConnection) connection), (HttpsServerConnection) connection); } catch (IOException e) { if (logger.isLoggable(Level.INFO)) { logThrow(logger, Level.INFO, HttpsListenHandle.class, "handleConnection", "{0} throws", new Object[] { connection }, e); } try { connection.close(); } catch (IOException e2) { } } } } /** * An HTTP server connection that gets its client host from the HTTPS * connection and closes the HTTPS connection when it shuts down. */ private static final class HttpServer extends HttpServerConnection { private final HttpsServerConnection connection; HttpServer(Socket socket, RequestDispatcher requestDispatcher, HttpsServerConnection connection) throws IOException { super(socket, requestDispatcher, httpServerManager); this.connection = connection; connection.httpServer = this; start(); } /** Not called -- handled by inbound request */ protected void checkPermissions() { throw new AssertionError(); } /** No additional context -- supplied by inbound request */ protected void populateContext(Collection context) { } /** Not called -- handled by inbound request */ protected InvocationConstraints checkConstraints( InvocationConstraints constraints) { throw new AssertionError(); } /** Schedules the connection to close after an idle timeout. */ protected void idle() { connectionTimer.scheduleTimeout(this, false); } /** Cancels the idle timeout. */ protected void busy() { connectionTimer.cancelTimeout(this); } /** Closes the associated secure connection. */ public boolean shutdown(boolean force) { boolean result = super.shutdown(force); if (result) { try { connection.close(); } catch (IOException e) { } } connectionTimer.cancelTimeout(this); return result; } } /** * Implements RequestDispatcher using the specified RequestDispatcher * and ServerConnection. */ private static final class HttpsRequestDispatcher extends Utilities implements RequestDispatcher { private final RequestDispatcher requestDispatcher; private final HttpsServerConnection connection; HttpsRequestDispatcher(RequestDispatcher requestDispatcher, HttpsServerConnection connection) { this.requestDispatcher = requestDispatcher; this.connection = connection; } public void dispatch(InboundRequest request) { try { InboundRequest wrappedRequest = new HttpsInboundRequest( request, connection, connection.processRequestData( request.getRequestInputStream(), request.getResponseOutputStream())); wrappedRequest.checkPermissions(); requestDispatcher.dispatch(wrappedRequest); } catch (SecurityException e) { if (logger.isLoggable(Level.INFO)) { logThrow( logger, Level.INFO, HttpsRequestDispatcher.class, "dispatch", "{0} throws", new Object[] { connection }, e); } } } } /** Implements InboundRequest */ private static final class HttpsInboundRequest implements InboundRequest { private final InboundRequest request; private final ServerConnection connection; private final InboundRequestHandle handle; HttpsInboundRequest(InboundRequest request, ServerConnection connection, InboundRequestHandle handle) { this.request = request; this.connection = connection; this.handle = handle; } /* Delegate to connection */ public void checkPermissions() { connection.checkPermissions(handle); } /* Delegate to connection */ public InvocationConstraints checkConstraints( InvocationConstraints constraints) throws UnsupportedConstraintException { return connection.checkConstraints(handle, constraints); } /* Delegate to both the underlying request and the connection. */ public void populateContext(Collection context) { request.populateContext(context); connection.populateContext(handle, context); } /* Delegate to underlying request */ public InputStream getRequestInputStream() { return request.getRequestInputStream(); } /* Delegate to underlying request */ public OutputStream getResponseOutputStream() { return request.getResponseOutputStream(); } /* Delegate to underlying request */ public void abort() { request.abort(); } } /** Implements ServerConnection. */ private final class HttpsServerConnection extends SslServerConnection { /** The HTTP server connection */ HttpServer httpServer; /** Creates a server connection. */ HttpsServerConnection(HttpsListenHandle listenHandle, Socket socket) throws IOException { super(listenHandle, socket); } void closeInternal(boolean removeFromListener) throws IOException { synchronized (this) { if (closed) { return; } } super.closeInternal(removeFromListener); /* Close the associated HTTP connection */ if (httpServer != null) { httpServer.shutdown(true); httpServer = null; } } } } }