You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2015/06/18 19:10:09 UTC
svn commit: r1686276 [1/2] - in /tomcat/trunk: ./
java/org/apache/catalina/core/ java/org/apache/tomcat/jni/
java/org/apache/tomcat/jni/socket/
Author: remm
Date: Thu Jun 18 17:10:08 2015
New Revision: 1686276
URL: http://svn.apache.org/r1686276
Log:
- Add JNI API updates from Netty and Twitter. jni.socket.* is not useful to Tomcat at the moment, but is a NIO2 style API on top of APR.
- Update recommended native library version to 1.2. If not using the new OpenSSL features, this shouldn't break and I prefer not requiring a trunk build.
Added:
tomcat/trunk/java/org/apache/tomcat/jni/CertificateVerifier.java
tomcat/trunk/java/org/apache/tomcat/jni/SSLExt.java
tomcat/trunk/java/org/apache/tomcat/jni/socket/
tomcat/trunk/java/org/apache/tomcat/jni/socket/AprSocket.java
tomcat/trunk/java/org/apache/tomcat/jni/socket/AprSocketContext.java
tomcat/trunk/java/org/apache/tomcat/jni/socket/HostInfo.java
Modified:
tomcat/trunk/NOTICE
tomcat/trunk/java/org/apache/catalina/core/AprLifecycleListener.java
tomcat/trunk/java/org/apache/tomcat/jni/SSL.java
tomcat/trunk/java/org/apache/tomcat/jni/SSLContext.java
Modified: tomcat/trunk/NOTICE
URL: http://svn.apache.org/viewvc/tomcat/trunk/NOTICE?rev=1686276&r1=1686275&r2=1686276&view=diff
==============================================================================
--- tomcat/trunk/NOTICE (original)
+++ tomcat/trunk/NOTICE Thu Jun 18 17:10:08 2015
@@ -4,6 +4,12 @@ Copyright 1999-2015 The Apache Software
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
+This software contains code derived from netty-native
+developed by the Netty project
+(http://netty.io, https://github.com/netty/netty-tcnative/)
+and from finagle-native developed at Twitter
+(https://github.com/twitter/finagle).
+
The Windows Installer is built with the Nullsoft
Scriptable Install System (NSIS), which is
open source software. The original software and
@@ -15,6 +21,13 @@ JDT Core Batch Compiler component, which
The original software and related information is available at
http://www.eclipse.org/jdt/core/.
+For portions of the Tomcat JNI OpenSSL API and the OpenSSL JSSE integration
+The org.apache.tomcat.jni and the org.apache.tomcat.net.openssl packages
+are derivative work originating from the Netty project and the finagle-native
+project developed at Twitter
+* Copyright 2014 The Netty Project
+* Copyright 2014 Twitter
+
The original XML Schemas for Java EE Deployment Descriptors:
- javaee_5.xsd
- javaee_web_services_1_2.xsd
Modified: tomcat/trunk/java/org/apache/catalina/core/AprLifecycleListener.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/AprLifecycleListener.java?rev=1686276&r1=1686275&r2=1686276&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/AprLifecycleListener.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/AprLifecycleListener.java Thu Jun 18 17:10:08 2015
@@ -68,8 +68,8 @@ public class AprLifecycleListener
protected static final int TCN_REQUIRED_MAJOR = 1;
protected static final int TCN_REQUIRED_MINOR = 1;
protected static final int TCN_REQUIRED_PATCH = 32;
- protected static final int TCN_RECOMMENDED_MINOR = 1;
- protected static final int TCN_RECOMMENDED_PV = 33;
+ protected static final int TCN_RECOMMENDED_MINOR = 2;
+ protected static final int TCN_RECOMMENDED_PV = 0;
// ---------------------------------------------- Properties
Added: tomcat/trunk/java/org/apache/tomcat/jni/CertificateVerifier.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/jni/CertificateVerifier.java?rev=1686276&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/jni/CertificateVerifier.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/jni/CertificateVerifier.java Thu Jun 18 17:10:08 2015
@@ -0,0 +1,34 @@
+/*
+ * 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.tomcat.jni;
+
+/**
+ * Is called during handshake and hooked into openssl via {@code SSL_CTX_set_cert_verify_callback}.
+ */
+public interface CertificateVerifier {
+
+ /**
+ * Returns {@code true} if the passed in certificate chain could be verified and so the handshake
+ * should be successful, {@code false} otherwise.
+ *
+ * @param ssl the SSL instance
+ * @param x509 the {@code X509} certificate chain
+ * @param authAlgorithm the auth algorithm
+ * @return verified {@code true} if verified successful, {@code false} otherwise
+ */
+ boolean verify(long ssl, byte[][] x509, String authAlgorithm);
+}
Modified: tomcat/trunk/java/org/apache/tomcat/jni/SSL.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/jni/SSL.java?rev=1686276&r1=1686275&r2=1686276&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/jni/SSL.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/jni/SSL.java Thu Jun 18 17:10:08 2015
@@ -226,6 +226,14 @@ public final class SSL {
* Add certificate chain number to that flag (0 ... verify depth)
*/
public static final int SSL_INFO_CLIENT_CERT_CHAIN = 0x0400;
+
+ /* Only support OFF and SERVER for now */
+ public static final long SSL_SESS_CACHE_OFF = 0x0000;
+ public static final long SSL_SESS_CACHE_SERVER = 0x0002;
+
+ public static final int SSL_SELECTOR_FAILURE_NO_ADVERTISE = 0;
+ public static final int SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL = 1;
+
/* Return OpenSSL version number */
public static native int version();
@@ -347,4 +355,301 @@ public final class SSL {
* @return true if all SSL_OP_* are supported by OpenSSL library.
*/
public static native boolean hasOp(int op);
+
+ /*
+ * Begin Twitter API additions
+ */
+
+ public static final int SSL_SENT_SHUTDOWN = 1;
+ public static final int SSL_RECEIVED_SHUTDOWN = 2;
+
+ public static final int SSL_ERROR_NONE = 0;
+ public static final int SSL_ERROR_SSL = 1;
+ public static final int SSL_ERROR_WANT_READ = 2;
+ public static final int SSL_ERROR_WANT_WRITE = 3;
+ public static final int SSL_ERROR_WANT_X509_LOOKUP = 4;
+ public static final int SSL_ERROR_SYSCALL = 5; /* look at error stack/return value/errno */
+ public static final int SSL_ERROR_ZERO_RETURN = 6;
+ public static final int SSL_ERROR_WANT_CONNECT = 7;
+ public static final int SSL_ERROR_WANT_ACCEPT = 8;
+
+ /**
+ * SSL_new
+ * @param ctx Server or Client context to use.
+ * @param server if true configure SSL instance to use accept handshake routines
+ * if false configure SSL instance to use connect handshake routines
+ * @return pointer to SSL instance (SSL *)
+ */
+ public static native long newSSL(long ctx, boolean server);
+
+ /**
+ * SSL_set_bio
+ * @param ssl SSL pointer (SSL *)
+ * @param rbio read BIO pointer (BIO *)
+ * @param wbio write BIO pointer (BIO *)
+ */
+ public static native void setBIO(long ssl, long rbio, long wbio);
+
+ /**
+ * SSL_get_error
+ * @param ssl SSL pointer (SSL *)
+ * @param ret TLS/SSL I/O return value
+ */
+ public static native int getError(long ssl, int ret);
+
+ /**
+ * BIO_ctrl_pending
+ * @param bio BIO pointer (BIO *)
+ * @return
+ */
+ public static native int pendingWrittenBytesInBIO(long bio);
+
+ /**
+ * SSL_pending
+ * @param ssl SSL pointer (SSL *)
+ * @return
+ */
+ public static native int pendingReadableBytesInSSL(long ssl);
+
+ /**
+ * BIO_write
+ * @param bio
+ * @param wbuf
+ * @param wlen
+ * @return
+ */
+ public static native int writeToBIO(long bio, long wbuf, int wlen);
+
+ /**
+ * BIO_read
+ * @param bio
+ * @param rbuf
+ * @param rlen
+ * @return
+ */
+ public static native int readFromBIO(long bio, long rbuf, int rlen);
+
+ /**
+ * SSL_write
+ * @param ssl the SSL instance (SSL *)
+ * @param wbuf
+ * @param wlen
+ * @return
+ */
+ public static native int writeToSSL(long ssl, long wbuf, int wlen);
+
+ /**
+ * SSL_read
+ * @param ssl the SSL instance (SSL *)
+ * @param rbuf
+ * @param rlen
+ * @return
+ */
+ public static native int readFromSSL(long ssl, long rbuf, int rlen);
+
+ /**
+ * SSL_get_shutdown
+ * @param ssl the SSL instance (SSL *)
+ * @return
+ */
+ public static native int getShutdown(long ssl);
+
+ /**
+ * SSL_set_shutdown
+ * @param ssl the SSL instance (SSL *)
+ * @param mode
+ */
+ public static native void setShutdown(long ssl, int mode);
+
+ /**
+ * SSL_free
+ * @param ssl the SSL instance (SSL *)
+ */
+ public static native void freeSSL(long ssl);
+
+ /**
+ * Wire up internal and network BIOs for the given SSL instance.
+ *
+ * <b>Warning: you must explicitly free this resource by calling freeBIO</b>
+ *
+ * While the SSL's internal/application data BIO will be freed when freeSSL is called on
+ * the provided SSL instance, you must call freeBIO on the returned network BIO.
+ *
+ * @param ssl the SSL instance (SSL *)
+ * @return pointer to the Network BIO (BIO *)
+ */
+ public static native long makeNetworkBIO(long ssl);
+
+ /**
+ * BIO_free
+ * @param bio
+ */
+ public static native void freeBIO(long bio);
+
+ /**
+ * BIO_flush
+ * @param bio
+ */
+ public static native void flushBIO(long bio);
+
+ /**
+ * SSL_shutdown
+ * @param ssl the SSL instance (SSL *)
+ * @return
+ */
+ public static native int shutdownSSL(long ssl);
+
+ /**
+ * Get the error number representing the last error OpenSSL encountered on this thread.
+ * @return
+ */
+ public static native int getLastErrorNumber();
+
+ /**
+ * SSL_get_cipher
+ * @param ssl the SSL instance (SSL *)
+ * @return
+ */
+ public static native String getCipherForSSL(long ssl);
+
+ /**
+ * SSL_get_version
+ * @param ssl the SSL instance (SSL *)
+ * @return
+ */
+ public static native String getVersion(long ssl);
+
+ /**
+ * SSL_do_handshake
+ * @param ssl the SSL instance (SSL *)
+ */
+ public static native int doHandshake(long ssl);
+
+ /**
+ * SSL_in_init
+ * @param SSL
+ * @return
+ */
+ public static native int isInInit(long SSL);
+
+ /**
+ * SSL_get0_next_proto_negotiated
+ * @param ssl the SSL instance (SSL *)
+ * @return
+ */
+ public static native String getNextProtoNegotiated(long ssl);
+
+ /*
+ * End Twitter API Additions
+ */
+
+ /**
+ * SSL_get0_alpn_selected
+ * @param ssl the SSL instance (SSL *)
+ * @return
+ */
+ public static native String getAlpnSelected(long ssl);
+
+ /**
+ * Get the peer certificate chain or {@code null} if non was send.
+ */
+ public static native byte[][] getPeerCertChain(long ssl);
+
+ /**
+ * Get the peer certificate or {@code null} if non was send.
+ */
+ public static native byte[] getPeerCertificate(long ssl);
+ /*
+ * Get the error number representing for the given {@code errorNumber}.
+ */
+ public static native String getErrorString(long errorNumber);
+
+ /**
+ * SSL_get_time
+ * @param ssl the SSL instance (SSL *)
+ * @return returns the time at which the session ssl was established. The time is given in seconds since the Epoch
+ */
+ public static native long getTime(long ssl);
+
+ /**
+ * Set Type of Client Certificate verification and Maximum depth of CA Certificates
+ * in Client Certificate verification.
+ * <br />
+ * This directive sets the Certificate verification level for the Client
+ * Authentication. Notice that this directive can be used both in per-server
+ * and per-directory context. In per-server context it applies to the client
+ * authentication process used in the standard SSL handshake when a connection
+ * is established. In per-directory context it forces a SSL renegotiation with
+ * the reconfigured client verification level after the HTTP request was read
+ * but before the HTTP response is sent.
+ * <br />
+ * The following levels are available for level:
+ * <pre>
+ * SSL_CVERIFY_NONE - No client Certificate is required at all
+ * SSL_CVERIFY_OPTIONAL - The client may present a valid Certificate
+ * SSL_CVERIFY_REQUIRE - The client has to present a valid Certificate
+ * SSL_CVERIFY_OPTIONAL_NO_CA - The client may present a valid Certificate
+ * but it need not to be (successfully) verifiable
+ * </pre>
+ * <br />
+ * The depth actually is the maximum number of intermediate certificate issuers,
+ * i.e. the number of CA certificates which are max allowed to be followed while
+ * verifying the client certificate. A depth of 0 means that self-signed client
+ * certificates are accepted only, the default depth of 1 means the client
+ * certificate can be self-signed or has to be signed by a CA which is directly
+ * known to the server (i.e. the CA's certificate is under
+ * {@code setCACertificatePath}, etc.
+ *
+ * @param ssl the SSL instance (SSL *)
+ * @param level Type of Client Certificate verification.
+ * @param depth Maximum depth of CA Certificates in Client Certificate
+ * verification.
+ */
+ public static native void setVerify(long ssl, int level, int depth);
+
+ /**
+ * Set OpenSSL Option.
+ * @param ssl the SSL instance (SSL *)
+ * @param options See SSL.SSL_OP_* for option flags.
+ */
+ public static native void setOptions(long ssl, int options);
+
+ /**
+ * Get OpenSSL Option.
+ * @param ssl the SSL instance (SSL *)
+ * @return options See SSL.SSL_OP_* for option flags.
+ */
+ public static native int getOptions(long ssl);
+
+ /**
+ * Returns all Returns the cipher suites that are available for negotiation in an SSL handshake.
+ * @param ssl the SSL instance (SSL *)
+ * @return ciphers
+ */
+ public static native String[] getCiphers(long ssl);
+
+ /**
+ * Returns the cipher suites available for negotiation in SSL handshake.
+ * <br />
+ * This complex directive uses a colon-separated cipher-spec string consisting
+ * of OpenSSL cipher specifications to configure the Cipher Suite the client
+ * is permitted to negotiate in the SSL handshake phase. Notice that this
+ * directive can be used both in per-server and per-directory context.
+ * In per-server context it applies to the standard SSL handshake when a
+ * connection is established. In per-directory context it forces a SSL
+ * renegotiation with the reconfigured Cipher Suite after the HTTP request
+ * was read but before the HTTP response is sent.
+ * @param ssl the SSL instance (SSL *)
+ * @param ciphers an SSL cipher specification
+ */
+ public static native boolean setCipherSuites(long ssl, String ciphers)
+ throws Exception;
+
+ /**
+ * Returns the ID of the session as byte array representation.
+ *
+ * @param ssl the SSL instance (SSL *)
+ * @return the session as byte array representation obtained via SSL_SESSION_get_id.
+ */
+ public static native byte[] getSessionId(long ssl);
}
Modified: tomcat/trunk/java/org/apache/tomcat/jni/SSLContext.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/jni/SSLContext.java?rev=1686276&r1=1686275&r2=1686276&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/jni/SSLContext.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/jni/SSLContext.java Thu Jun 18 17:10:08 2015
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.tomcat.jni;
import java.util.Map;
@@ -96,6 +97,13 @@ public final class SSLContext {
public static native void setOptions(long ctx, int options);
/**
+ * Get OpenSSL Option.
+ * @param ctx Server or Client context to use.
+ * @return options See SSL.SSL_OP_* for option flags.
+ */
+ public static native int getOptions(long ctx);
+
+ /**
* Clears OpenSSL Options.
* @param ctx Server or Client context to use.
* @param options See SSL.SSL_OP_* for option flags.
@@ -214,6 +222,62 @@ public final class SSLContext {
throws Exception;
/**
+ * Set the size of the internal session cache.
+ * http://www.openssl.org/docs/ssl/SSL_CTX_sess_set_cache_size.html
+ */
+ public static native long setSessionCacheSize(long ctx, long size);
+
+ /**
+ * Get the size of the internal session cache.
+ * http://www.openssl.org/docs/ssl/SSL_CTX_sess_get_cache_size.html
+ */
+ public static native long getSessionCacheSize(long ctx);
+
+ /**
+ * Set the timeout for the internal session cache in seconds.
+ * http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html
+ */
+ public static native long setSessionCacheTimeout(long ctx, long timeoutSeconds);
+
+ /**
+ * Get the timeout for the internal session cache in seconds.
+ * http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html
+ */
+ public static native long getSessionCacheTimeout(long ctx);
+
+ /**
+ * Set the mode of the internal session cache and return the previous used mode.
+ */
+ public static native long setSessionCacheMode(long ctx, long mode);
+
+ /**
+ * Get the mode of the current used internal session cache.
+ */
+ public static native long getSessionCacheMode(long ctx);
+
+ /**
+ * Session resumption statistics methods.
+ * http://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html
+ */
+ public static native long sessionAccept(long ctx);
+ public static native long sessionAcceptGood(long ctx);
+ public static native long sessionAcceptRenegotiate(long ctx);
+ public static native long sessionCacheFull(long ctx);
+ public static native long sessionCbHits(long ctx);
+ public static native long sessionConnect(long ctx);
+ public static native long sessionConnectGood(long ctx);
+ public static native long sessionConnectRenegotiate(long ctx);
+ public static native long sessionHits(long ctx);
+ public static native long sessionMisses(long ctx);
+ public static native long sessionNumber(long ctx);
+ public static native long sessionTimeouts(long ctx);
+
+ /**
+ * Set TLS session keys. This allows us to share keys across TFEs.
+ */
+ public static native void setSessionTicketKeys(long ctx, byte[] keys);
+
+ /**
* Set File and Directory of concatenated PEM-encoded CA Certificates
* for Client Auth
* <br>
@@ -377,4 +441,72 @@ public final class SSLContext {
*/
public long getSslContext(String sniHostName);
}
+
+ /**
+ * Allow to hook {@link CertificateVerifier} into the handshake processing.
+ * This will call {@code SSL_CTX_set_cert_verify_callback} and so replace the default verification
+ * callback used by openssl
+ * @param ctx Server or Client context to use.
+ * @param verifier the verifier to call during handshake.
+ */
+ public static native void setCertVerifyCallback(long ctx, CertificateVerifier verifier);
+
+ /**
+ * Set next protocol for next protocol negotiation extension
+ * @param ctx Server context to use.
+ * @param nextProtos comma delimited list of protocols in priority order
+ *
+ * @deprecated use {@link #setNpnProtos(long, String[], int)}
+ */
+ @Deprecated
+ public static void setNextProtos(long ctx, String nextProtos) {
+ setNpnProtos(ctx, nextProtos.split(","), SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL);
+ }
+
+ /**
+ * Set next protocol for next protocol negotiation extension
+ * @param ctx Server context to use.
+ * @param nextProtos protocols in priority order
+ * @param selectorFailureBehavior see {@link SSL#SSL_SELECTOR_FAILURE_NO_ADVERTISE}
+ * and {@link SSL#SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL}
+ */
+ public static native void setNpnProtos(long ctx, String[] nextProtos, int selectorFailureBehavior);
+
+ /**
+ * Set application layer protocol for application layer protocol negotiation extension
+ * @param ctx Server context to use.
+ * @param alpnProtos protocols in priority order
+ * @param selectorFailureBehavior see {@link SSL#SSL_SELECTOR_FAILURE_NO_ADVERTISE}
+ * and {@link SSL#SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL}
+ */
+ public static native void setAlpnProtos(long ctx, String[] alpnProtos, int selectorFailureBehavior);
+
+ /**
+ * Set DH parameters
+ * @param ctx Server context to use.
+ * @param cert DH param file (can be generated from e.g. {@code openssl dhparam -rand - 2048 > dhparam.pem} -
+ * see the <a href="https://www.openssl.org/docs/apps/dhparam.html">OpenSSL documentation</a>).
+ */
+ public static native void setTmpDH(long ctx, String cert)
+ throws Exception;
+
+ /**
+ * Set ECDH elliptic curve by name
+ * @param ctx Server context to use.
+ * @param curveName the name of the elliptic curve to use
+ * (available names can be obtained from {@code openssl ecparam -list_curves}).
+ */
+ public static native void setTmpECDHByCurveName(long ctx, String curveName)
+ throws Exception;
+
+ /**
+ * Set the context within which session be reused (server side only)
+ * http://www.openssl.org/docs/ssl/SSL_CTX_set_session_id_context.html
+ *
+ * @param ctx Server context to use.
+ * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name
+ * of the application and/or the hostname and/or service name
+ * @return {@code true} if success, {@code false} otherwise.
+ */
+ public static native boolean setSessionIdContext(long ctx, byte[] sidCtx);
}
Added: tomcat/trunk/java/org/apache/tomcat/jni/SSLExt.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/jni/SSLExt.java?rev=1686276&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/jni/SSLExt.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/jni/SSLExt.java Thu Jun 18 17:10:08 2015
@@ -0,0 +1,159 @@
+/*
+ * 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.tomcat.jni;
+
+/**
+ * Support TLS extensions and extra methods.
+ *
+ * The methods are separated to make it easier for java code to
+ * support existing native library - it can check if this class can
+ * be loaded in order to use the exensions.
+ *
+ * @author Costin Manolache
+ */
+public final class SSLExt {
+
+
+ /**
+ * Set advertised NPN protocol.
+ * This is only available for recent or patched openssl.
+ *
+ * Example: "\x06spdy/2"
+ *
+ * Works with TLS1, doesn't with SSL2/SSL3
+ *
+ * Servers sends list in ServerHelo, client selects it and
+ * sends it back after ChangeChipher
+ *
+ * Not supported in 1.0.0, seems to be in 1.0.1 and after
+ */
+ public static native int setNPN(long tcctx, byte[] proto, int len);
+
+ /**
+ * Get other side's advertised protocols.
+ * Only works after handshake.
+ */
+ public static native int getNPN(long tcsock, byte[] proto);
+
+ /**
+ * Enabling dump/debugging on the socket. Both raw and decrypted
+ * packets will be logged.
+ */
+ public static native int debug(long tcsock);
+
+ /**
+ * Server: Extract the session data associated with the socket.
+ * Must be saved, keyed by session ID.
+ */
+ public static native byte[] getSessionData(long tcsock);
+
+ /**
+ * Server: Set the session data for a socket.
+ */
+ public static native int setSessionData(long tcsock, byte[] data, int len);
+
+
+ /**
+ * Client: get the ticket received from server, if tickets are supported.
+ */
+ public static native int getTicket(long tcsock, byte[] resBuf);
+
+ /**
+ * Client: set the previously received ticket.
+ */
+ public static native int setTicket(long tcsock, byte[] data, int len);
+
+ /**
+ * Set the key used by server to generate tickets.
+ * Key must be 48 bytes.
+ */
+ public static native int setTicketKeys(long ctx, byte[] data, int len);
+
+ /**
+ * For client side calls. Data should be a \0 terminated string
+ */
+ public static native int setSNI(long tcsock, byte[] data, int len);
+
+ /**
+ * Return the last openssl error
+ */
+ public static native String sslErrReasonErrorString();
+
+ public static native long sslCtxSetMode(long ctx, long mode);
+
+ /* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success
+ * when just a single record has been written): */
+ public static final int SSL_MODE_ENABLE_PARTIAL_WRITE = 0x1;
+
+ /* Make it possible to retry SSL_write() with changed buffer location
+ * (buffer contents must stay the same!); this is not the default to avoid
+ * the misconception that non-blocking SSL_write() behaves like
+ * non-blocking write(): */
+ public static final int SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = 0x2;
+
+ /* Don't attempt to automatically build certificate chain */
+ static final int SSL_MODE_NO_AUTO_CHAIN = 0x8;
+
+ /* Save RAM by releasing read and write buffers when they're empty. (SSL3 and
+ * TLS only.) "Released" buffers are put onto a free-list in the context
+ * or just freed (depending on the context's setting for freelist_max_len). */
+ public static final int SSL_MODE_RELEASE_BUFFERS = 0x10;
+
+ // 1.1
+ //static final int SSL_MODE_HANDSHAKE_CUTTHROUGH = ..;
+
+ /**
+ * SSL_set_mode
+ */
+ public static native long sslSetMode(long tcsock, long mode);
+
+ public static int setNPN(long sslContext, byte[] spdyNPN) {
+ try {
+ return SSLExt.setNPN(sslContext, spdyNPN, spdyNPN.length);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return -1;
+ }
+ }
+
+ /**
+ * Higher level method, checking if the specified protocol has been
+ * negotiated.
+ */
+ public static boolean checkNPN(long tcsocket, byte[] expected) {
+ byte[] npn = new byte[expected.length + 1];
+ int npnLen = 0;
+ try {
+ npnLen = SSLExt.getNPN(tcsocket, npn);
+ if (npnLen != expected.length) {
+ return false;
+ }
+ } catch (Throwable t) {
+ // ignore
+ return false;
+ }
+ for (int i = 0; i < expected.length; i++) {
+ if (expected[i] != npn[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+
+}
Added: tomcat/trunk/java/org/apache/tomcat/jni/socket/AprSocket.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/jni/socket/AprSocket.java?rev=1686276&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/jni/socket/AprSocket.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/jni/socket/AprSocket.java Thu Jun 18 17:10:08 2015
@@ -0,0 +1,922 @@
+/*
+ * 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.tomcat.jni.socket;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tomcat.jni.Address;
+import org.apache.tomcat.jni.Error;
+import org.apache.tomcat.jni.Poll;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLExt;
+import org.apache.tomcat.jni.SSLSocket;
+import org.apache.tomcat.jni.Sockaddr;
+import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.jni.Status;
+import org.apache.tomcat.jni.socket.AprSocketContext.AprPoller;
+import org.apache.tomcat.jni.socket.AprSocketContext.BlockingPollHandler;
+
+/**
+ * Native socket, using JNI + APR + openssl.
+ *
+ * The socket is non-blocking - you can register either a blocking or non
+ * blocking callback.
+ *
+ * There is no explicit method to register/unregister poll interest -
+ * it is done automatically, when read/write methods return 0.
+ *
+ * To keep the socket polling you must read all the available data, until
+ * read() returns 0. If you want to pause - don't read all input. To resume -
+ * read again until it returns 0.
+ *
+ * Same for write - when write() returns 0 the socket is registered for
+ * write interest.
+ *
+ * You can also use the blocking read/write methods.
+ */
+public class AprSocket implements Runnable {
+
+ private static final Logger log =
+ Logger.getLogger("org.apache.tomcat.jni.socket.AprSocket");
+
+ private static final byte[][] NO_CERTS = new byte[0][];
+
+ static final int CONNECTING = 0x1;
+ static final int CONNECTED = 0x2;
+
+ // Current ( real ) poll status
+ static final int POLLIN_ACTIVE = 0x4;
+ static final int POLLOUT_ACTIVE = 0x8;
+
+ static final int POLL = 0x10;
+
+ static final int SSL_ATTACHED = 0x40;
+
+ // Requested poll status. Set by read/write when needed.
+ // Cleared when polled
+ static final int POLLIN = 0x80;
+ static final int POLLOUT = 0x100;
+
+ static final int ACCEPTED = 0x200;
+ static final int ERROR = 0x400;
+ static final int CLOSED = 0x800;
+
+ static final int READING = 0x1000;
+ static final int WRITING = 0x2000;
+
+ // Not null
+ private final AprSocketContext context;
+
+ // only one - to save per/socket memory - context has similar callbacks.
+ BlockingPollHandler handler;
+
+ // Set while it's associated with a poller - it'll stay associated after
+ // connect until close. Destroy will happen in the poller.
+ // POLL bit indicates if the socket is actually polling.
+ AprPoller poller;
+
+ // Bit field indicating the status and socket should only be accessed with
+ // socketLock protection
+ private int status;
+
+ long socket;
+
+ //long to = 10000;
+
+ // Persistent info about the peer ( SSL, etc )
+ private HostInfo hostInfo;
+
+ AprSocket(AprSocketContext context) {
+ this.context = context;
+ }
+
+ public void recycle() {
+ status = 0;
+ hostInfo = null;
+ handler = null;
+ socket = 0;
+ poller = null;
+ }
+
+ @Override
+ public String toString() {
+ return (context.isServer() ? "AprSrv-" : "AprCli-") +
+ Long.toHexString(socket) + " " + Integer.toHexString(status);
+ }
+
+ public void setHandler(BlockingPollHandler l) {
+ handler = l;
+ }
+
+ private void setNonBlocking() {
+ if (socket != 0 && context.running) {
+ Socket.optSet(socket, Socket.APR_SO_NONBLOCK, 1);
+ Socket.timeoutSet(socket, 0);
+ }
+ }
+
+ /**
+ * Check if the socket is currently registered with a poller.
+ */
+ public boolean isPolling() {
+ synchronized (this) {
+ return (status & POLL) != 0;
+ }
+ }
+
+ public BlockingPollHandler getHandler() {
+ return handler;
+ }
+
+ public AprSocketContext getContext() {
+ return context;
+ }
+
+ AprSocket setHost(HostInfo hi) {
+ hostInfo = hi;
+ return this;
+ }
+
+ /**
+ */
+ public void connect() throws IOException {
+ if (isBlocking()) {
+ // will call handleConnected() at the end.
+ context.connectBlocking(this);
+ } else {
+ synchronized(this) {
+ if ((status & CONNECTING) != 0) {
+ return;
+ }
+ status |= CONNECTING;
+ }
+ context.connectExecutor.execute(this);
+ }
+ }
+
+
+ // after connection is done, called from a thread pool ( not IO thread )
+ // may block for handshake.
+ void afterConnect() throws IOException {
+ if (hostInfo.secure) {
+ blockingStartTLS();
+ }
+
+ setNonBlocking(); // call again, to set the bits ( connect was blocking )
+
+ setStatus(CONNECTED);
+ clearStatus(CONNECTING);
+
+ notifyConnected(false);
+ }
+
+ public HostInfo getHost() {
+ return hostInfo;
+ }
+
+ /**
+ * Write as much data as possible to the socket.
+ *
+ * @param data
+ * @param off
+ * @param len
+ * @return For both blocking and non-blocking, returns the number of bytes
+ * written. If no data can be written (e.g. if the buffers are
+ * full) 0 will be returned.
+ * @throws IOException
+ */
+ public int write(byte[] data, int off, int len, long to) throws IOException {
+ long max = System.currentTimeMillis() + to;
+
+ while (true) {
+ int rc = writeInternal(data, off, len);
+ if (rc < 0) {
+ throw new IOException("Write error " + rc);
+ } else if (rc == 0) {
+ // need poll out - do we need to update polling ?
+ context.findPollerAndAdd(this);
+ } else {
+ return rc;
+ }
+
+ try {
+ long waitTime = max - System.currentTimeMillis();
+ if (waitTime <= 0) {
+ return 0;
+ }
+ wait(waitTime);
+ } catch (InterruptedException e) {
+ return 0;
+ }
+ }
+ }
+
+ public int write(byte[] data, int off, int len) throws IOException {
+ // In SSL mode, read/write can't be called at the same time.
+ int rc = writeInternal(data, off, len);
+ if (rc < 0) {
+ throw new IOException("Write error " + rc);
+ } else if (rc == 0) {
+ // need poll out - do we need to update polling ?
+ synchronized (this) {
+ context.findPollerAndAdd(this);
+ }
+ }
+ return rc;
+ }
+
+ private int writeInternal(byte[] data, int off, int len) throws IOException {
+ int rt = 0;
+ int sent = 0;
+ synchronized(this) {
+ if ((status & CLOSED) != 0
+ || socket == 0
+ || !context.running) {
+ throw new IOException("Closed");
+ }
+ if ((status & WRITING) != 0) {
+ throw new IOException("Write from 2 threads not allowed");
+ }
+ status |= WRITING;
+
+ while (len > 0) {
+ sent = Socket.send(socket, data, off, len);
+ if (sent <= 0) {
+ break;
+ }
+ off += sent;
+ len -= sent;
+ }
+
+ status &= ~WRITING;
+ }
+
+ if (context.rawDataHandler != null) {
+ context.rawData(this, false, data, off, sent, len, false);
+ }
+
+ if (sent <= 0) {
+ if (sent == -Status.TIMEUP || sent == -Status.EAGAIN || sent == 0) {
+ setStatus(POLLOUT);
+ updatePolling();
+ return rt;
+ }
+ log.warning("apr.send(): Failed to send, closing " + sent);
+ reset();
+ throw new IOException("Error sending " + sent + " " + Error.strerror(-sent));
+ } else {
+ off += sent;
+ len -= sent;
+ rt += sent;
+ return sent;
+ }
+ }
+
+ public int read(byte[] data, int off, int len, long to) throws IOException {
+ int rd = readNB(data, off, len);
+ if (rd == 0) {
+ synchronized(this) {
+ try {
+ wait(to);
+ } catch (InterruptedException e) {
+ return 0;
+ }
+ }
+ rd = readNB(data, off, len);
+ }
+ return processReadResult(data, off, len, rd);
+ }
+
+ public int read(byte[] data, int off, int len) throws IOException {
+ return readNB(data, off, len);
+ }
+
+ private int processReadResult(byte[] data, int off, int len, int read)
+ throws IOException {
+ if (context.rawDataHandler != null) {
+ context.rawData(this, true, data, off, read, len, false);
+ }
+
+ if (read > 0) {
+ return read;
+ }
+
+ if (read == 0 || read == -Status.TIMEUP || read == -Status.ETIMEDOUT
+ || read == -Status.EAGAIN) {
+ read = 0;
+ setStatus(POLLIN);
+ updatePolling();
+ return 0;
+ }
+
+ if (read == -Status.APR_EOF || read == -1) {
+ close();
+ return -1;
+ }
+ // abrupt close
+ reset();
+ throw new IOException("apr.read(): " + read + " " + Error.strerror(-read));
+ }
+
+ public int readNB(byte[] data, int off, int len) throws IOException {
+ int read;
+ synchronized(this) {
+ if ((status & CLOSED) != 0
+ || socket == 0
+ || !context.running) {
+ return -1;
+ }
+ if ((status & READING) != 0) {
+ throw new IOException("Read from 2 threads not allowed");
+ }
+ status |= READING;
+
+ read = Socket.recv(socket, data, off, len);
+ status &= ~READING;
+ }
+ return processReadResult(data, off, len, read);
+ }
+
+ /*
+ No support for shutdownOutput: SSL is quite tricky.
+ Use close() instead - no read/write will be allowed after.
+
+ */
+
+ public void close() {
+ synchronized (this) {
+ if ((status & CLOSED) != 0 || socket == 0) {
+ return;
+ }
+ status |= CLOSED;
+ status &= ~POLLIN;
+ status &= ~POLLOUT;
+ }
+ if (context.rawDataHandler != null) {
+ context.rawDataHandler.rawData(this, false, null, 0, 0, 0, true);
+ }
+ Socket.close(socket);
+ if (poller == null) {
+ maybeDestroy();
+ } else {
+ try {
+ poller.requestUpdate(this);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ void maybeDestroy() {
+ synchronized(this) {
+ if (socket == 0 ||
+ (status & CONNECTING) != 0 || !context.running) {
+ // closed or operation in progress
+ // if context stopped, pool will be destroyed and close
+ // all sockets automatically.
+ return;
+ }
+ if ((status & CLOSED) == 0) {
+ return; // not closed
+ }
+ if ((status & (WRITING | READING)) != 0) {
+ return; // not closed
+ }
+
+ if (context.rawDataHandler != null) {
+ context.rawDataHandler.rawData(this, false, null, -1, -1, -1, true);
+ }
+ if (log.isLoggable(Level.FINE)) {
+ log.info("closing: context.open=" + context.open.get() + " " + this);
+ }
+
+ context.open.decrementAndGet();
+
+ if (socket != 0 && (status & CLOSED) == 0) {
+ Socket.close(socket);
+ status |= CLOSED;
+ }
+
+ if (handler != null) {
+ if (isBlocking()) {
+ context.getExecutor().execute(this);
+ } else {
+ handler.closed(this);
+ }
+ }
+
+ context.destroySocket(this);
+ }
+ }
+
+
+
+ /**
+ * Close input and output, potentially sending RST, than close the socket.
+ *
+ * The proper way to close when gracefully done is by calling writeEnd() and
+ * reading all remaining input until -1 (EOF) is received.
+ *
+ * If EOF is received, the proper way to close is send whatever is remaining and
+ * call writeEnd();
+ */
+ public void reset() {
+ setStatus(ERROR);
+ close();
+ }
+
+
+ /**
+ */
+ public boolean isClosed() {
+ synchronized(this) {
+ if ((status & CLOSED) != 0 || socket == 0 || !context.running) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public long getIOTimeout() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ return Socket.timeoutGet(socket) / 1000;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ } else {
+ throw new IOException("Socket is closed");
+ }
+ }
+
+ // Cert is in DER format
+ // Called after handshake
+ public byte[][] getPeerCert(boolean check) throws IOException {
+ getHost();
+ if (hostInfo.certs != null && hostInfo.certs != NO_CERTS && !check) {
+ return hostInfo.certs;
+ }
+ if (!checkBitAndSocket(SSL_ATTACHED)) {
+ return NO_CERTS;
+ }
+ try {
+ int certLength = SSLSocket.getInfoI(socket,
+ SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+ // TODO: if resumed, old certs are good.
+ // If not - warn if certs changed, remember first cert, etc.
+ if (certLength <= 0) {
+ // Can also happen on session resume - keep the old
+ if (hostInfo.certs == null) {
+ hostInfo.certs = NO_CERTS;
+ }
+ return hostInfo.certs;
+ }
+ hostInfo.certs = new byte[certLength + 1][];
+
+ hostInfo.certs[0] = SSLSocket.getInfoB(socket,
+ SSL.SSL_INFO_CLIENT_CERT);
+ for (int i = 0; i < certLength; i++) {
+ hostInfo.certs[i + 1] = SSLSocket.getInfoB(socket,
+ SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+ }
+ return hostInfo.certs;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ public X509Certificate[] getPeerX509Cert() throws IOException {
+ byte[][] certs = getPeerCert(false);
+ X509Certificate[] xcerts = new X509Certificate[certs.length];
+ if (certs.length == 0) {
+ return xcerts;
+ }
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ for (int i = 0; i < certs.length; i++) {
+ if (certs[i] != null) {
+ ByteArrayInputStream bis = new ByteArrayInputStream(
+ certs[i]);
+ xcerts[i] = (X509Certificate) cf.generateCertificate(bis);
+ bis.close();
+ }
+ }
+ } catch (CertificateException ex) {
+ throw new IOException(ex);
+ }
+ return xcerts;
+ }
+
+ public String getCipherSuite() throws IOException {
+ if (checkBitAndSocket(SSL_ATTACHED)) {
+ return null;
+ }
+ try {
+ return SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ public int getKeySize() throws IOException {
+ if (checkBitAndSocket(SSL_ATTACHED)) {
+ return -1;
+ }
+ try {
+ return SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ public int getRemotePort() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ long sa = Address.get(Socket.APR_REMOTE, socket);
+ Sockaddr addr = Address.getInfo(sa);
+ return addr.port;
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+ throw new IOException("Socket closed");
+ }
+
+ public String getRemoteAddress() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ long sa = Address.get(Socket.APR_REMOTE, socket);
+ return Address.getip(sa);
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+ throw new IOException("Socket closed");
+ }
+
+ public String getRemoteHostname() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ long sa = Address.get(Socket.APR_REMOTE, socket);
+ String remoteHost = Address.getnameinfo(sa, 0);
+ if (remoteHost == null) {
+ remoteHost = Address.getip(sa);
+ }
+ return remoteHost;
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+ throw new IOException("Socket closed");
+ }
+
+ public int getLocalPort() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ long sa = Address.get(Socket.APR_LOCAL, socket);
+ Sockaddr addr = Address.getInfo(sa);
+ return addr.port;
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+ throw new IOException("Socket closed");
+ }
+
+ public String getLocalAddress() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ long sa = Address.get(Socket.APR_LOCAL, socket);
+ return Address.getip(sa);
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+ throw new IOException("Socket closed");
+ }
+
+ public String getLocalHostname() throws IOException {
+ if (socket != 0 && context.running) {
+ try {
+ long sa = Address.get(Socket.APR_LOCAL, socket);
+ return Address.getnameinfo(sa, 0);
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+ throw new IOException("Socket closed");
+ }
+
+ public boolean isBlocking() {
+ return ! (handler instanceof AprSocketContext.NonBlockingPollHandler);
+ }
+
+ public boolean isError() {
+ return checkPreConnect(ERROR);
+ }
+
+ void notifyError(Throwable err, boolean needsThread) {
+ if (handler instanceof AprSocketContext.NonBlockingPollHandler) {
+ if (err != null) {
+ ((AprSocketContext.NonBlockingPollHandler) handler).error(this, err);
+ }
+ } else {
+ // poller destroyed, etc
+ if (needsThread) {
+ context.getExecutor().execute(this);
+ } else {
+ try {
+ notifyIO();
+ } catch (IOException e) {
+ log.log(Level.SEVERE, this + " error ", e);
+ }
+ }
+ }
+ }
+
+ // Called after connect and from poller.
+ void notifyIO() throws IOException {
+ long t0 = System.currentTimeMillis();
+ try {
+ if (handler != null) {
+ handler.process(this, true, false, false);
+ }
+ } catch (Throwable t) {
+ throw new IOException(t);
+ } finally {
+ long t1 = System.currentTimeMillis();
+ t1 -= t0;
+ if (t1 > context.maxHandlerTime.get()) {
+ context.maxHandlerTime.set(t1);
+ }
+ context.totalHandlerTime.addAndGet(t1);
+ context.handlerCount.incrementAndGet();
+ }
+ }
+
+ private void notifyConnected(boolean server) throws IOException {
+ // Will set the handler on the channel for accepted
+ context.onSocket(this);
+
+ if (handler instanceof AprSocketContext.NonBlockingPollHandler) {
+ ((AprSocketContext.NonBlockingPollHandler) handler).connected(this);
+
+ ((AprSocketContext.NonBlockingPollHandler) handler).process(this, true, true, false);
+ // Now register for polling - unless process() set suspendRead and
+ // doesn't need out notifications
+ updatePolling();
+ } else {
+ if (server) {
+ // client will block in connect().
+ // Server: call process();
+ notifyIO();
+ }
+ }
+ }
+
+ private void updatePolling() throws IOException {
+ synchronized (this) {
+ if ((status & CLOSED) != 0) {
+ maybeDestroy();
+ return;
+ }
+ }
+ context.findPollerAndAdd(this);
+ }
+
+ @Override
+ public void run() {
+ if (!context.running) {
+ return;
+ }
+ if (checkPreConnect(CLOSED)) {
+ if (handler != null) {
+ handler.closed(this);
+ }
+ return;
+ }
+ if (!checkPreConnect(CONNECTED)) {
+ if (checkBitAndSocket(ACCEPTED)) {
+ try {
+ context.open.incrementAndGet();
+
+ if (log.isLoggable(Level.FINE)) {
+ log.info("Accept: " + context.open.get() + " " + this + " " +
+ getRemotePort());
+ }
+ if (context.tcpNoDelay) {
+ Socket.optSet(socket, Socket.APR_TCP_NODELAY, 1);
+ }
+
+ setStatus(CONNECTED);
+ if (context.sslMode) {
+ Socket.timeoutSet(socket,
+ context.connectTimeout * 1000L);
+ blockingStartTLS();
+ }
+ setNonBlocking(); // call again, to set the bits ( connect was blocking )
+
+ notifyConnected(true);
+ return;
+ } catch (Throwable t) {
+ t.printStackTrace(); // no error handler yet
+ reset();
+ notifyError(t, false);
+ return;
+ }
+ }
+ if (checkPreConnect(CONNECTING)) {
+ // Non-blocking connect - will call 'afterConnection' at the end.
+ try {
+ context.connectBlocking(this);
+ } catch (IOException t) {
+ reset(); // also sets status ERROR
+ if (handler instanceof AprSocketContext.NonBlockingPollHandler) {
+ ((AprSocketContext.NonBlockingPollHandler) handler).process(this, false, false, true);
+ }
+ notifyError(t, false);
+ }
+ }
+ } else {
+ if (handler != null) {
+ try {
+ notifyIO();
+ } catch (Throwable e) {
+ log.log(Level.SEVERE, this + " error ", e);
+ reset();
+ // no notifyIO - just did it.
+ }
+ }
+ }
+ }
+
+ /**
+ * This is a blocking call ! ( can be made non-blocking, but too complex )
+ *
+ * Will be called automatically after connect() or accept if 'secure' is
+ * true.
+ *
+ * Can be called manually to upgrade the channel
+ * @throws IOException
+ */
+ public void blockingStartTLS() throws IOException {
+ synchronized(this) {
+ if (socket == 0 || !context.running) {
+ return;
+ }
+ if ((status & SSL_ATTACHED) != 0) {
+ return;
+ }
+ status |= SSL_ATTACHED;
+ }
+
+ try {
+ if (log.isLoggable(Level.FINE)) {
+ log.info(this + " StartSSL");
+ }
+
+ AprSocketContext aprCon = context;
+ SSLSocket.attach(aprCon.getSslCtx(), socket);
+
+ if (context.debugSSL) {
+ SSLExt.debug(socket);
+ }
+ if (!getContext().isServer()) {
+ if (context.USE_TICKETS && hostInfo.ticketLen > 0) {
+ SSLExt.setTicket(socket, hostInfo.ticket,
+ hostInfo.ticketLen);
+ } else if (hostInfo.sessDer != null) {
+ SSLExt.setSessionData(socket, hostInfo.sessDer,
+ hostInfo.sessDer.length);
+ }
+ }
+ SSLExt.sslSetMode(socket, SSLExt.SSL_MODE_ENABLE_PARTIAL_WRITE |
+ SSLExt.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ int rc = SSLSocket.handshake(socket);
+
+ // At this point we have the session ID, remote certs, etc
+ // we can lookup host info
+ if (hostInfo == null) {
+ hostInfo = new HostInfo();
+ }
+
+ if (rc != Status.APR_SUCCESS) {
+ throw new IOException(this + " Handshake failed " + rc + " "
+ + Error.strerror(rc) + " SSLL "
+ + SSL.getLastError());
+ } else { // SUCCESS
+ handshakeDone();
+ }
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void handshakeDone() throws IOException {
+ getHost();
+ if (socket == 0 || !context.running) {
+ throw new IOException("Socket closed");
+ }
+ if (context.USE_TICKETS && ! context.isServer()) {
+ if (hostInfo.ticket == null) {
+ hostInfo.ticket = new byte[2048];
+ }
+ int ticketLen = SSLExt.getTicket(socket, hostInfo.ticket);
+ if (ticketLen > 0) {
+ hostInfo.ticketLen = ticketLen;
+ if (log.isLoggable(Level.FINE)) {
+ log.info("Received ticket: " + ticketLen);
+ }
+ }
+ }
+
+ // TODO: if the ticket, session id or session changed - callback to
+ // save the session again
+ try {
+ hostInfo.sessDer = SSLExt.getSessionData(socket);
+ getPeerCert(true);
+ hostInfo.sessionId = SSLSocket.getInfoS(socket,
+ SSL.SSL_INFO_SESSION_ID);
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+
+ hostInfo.npn = new byte[32];
+ hostInfo.npnLen = SSLExt.getNPN(socket, hostInfo.npn);
+
+ // If custom verification is used - should check the certificates
+ if (context.tlsCertVerifier != null) {
+ context.tlsCertVerifier.handshakeDone(this);
+ }
+ }
+
+ int requestedPolling() {
+ synchronized(this) {
+ if (socket == 0 || ((status & CLOSED) != 0)) {
+ return 0;
+ }
+ // Implicit:
+ //Poll.APR_POLLNVAL | Poll.APR_POLLHUP | Poll.APR_POLLERR |
+ int res = 0;
+ if ((status & POLLIN) != 0) {
+ res = Poll.APR_POLLIN;
+ }
+ if ((status & POLLOUT) != 0) {
+ res |= Poll.APR_POLLOUT;
+ }
+ return res;
+ }
+ }
+
+ boolean checkBitAndSocket(int bit) {
+ synchronized (this) {
+ return ((status & bit) != 0 && socket != 0 &&
+ (status & CLOSED) == 0 && context.running);
+ }
+ }
+
+ boolean checkPreConnect(int bit) {
+ synchronized (this) {
+ return ((status & bit) != 0);
+ }
+ }
+
+ void clearStatus(int bit) {
+ synchronized (this) {
+ status &= ~bit;
+ }
+ }
+
+ boolean setStatus(int bit) {
+ synchronized (this) {
+ int old = status & bit;
+ status |= bit;
+ return old != 0;
+ }
+ }
+
+
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org