summaryrefslogtreecommitdiff
path: root/ACE/ace/SSL/SSL_SOCK_Connector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/SSL/SSL_SOCK_Connector.cpp')
-rw-r--r--ACE/ace/SSL/SSL_SOCK_Connector.cpp411
1 files changed, 411 insertions, 0 deletions
diff --git a/ACE/ace/SSL/SSL_SOCK_Connector.cpp b/ACE/ace/SSL/SSL_SOCK_Connector.cpp
new file mode 100644
index 00000000000..41e00ff6f4a
--- /dev/null
+++ b/ACE/ace/SSL/SSL_SOCK_Connector.cpp
@@ -0,0 +1,411 @@
+// -*- C++ -*-
+//
+// $Id$
+
+#include "SSL_SOCK_Connector.h"
+
+#include "ace/OS_NS_errno.h"
+#include "ace/Handle_Set.h"
+#include "ace/INET_Addr.h"
+#include "ace/Log_Msg.h"
+#include "ace/Countdown_Time.h"
+
+#include <openssl/err.h>
+
+#if !defined (__ACE_INLINE__)
+#include "SSL_SOCK_Connector.i"
+#endif /* __ACE_INLINE__ */
+
+ACE_RCSID (ACE_SSL,
+ SSL_SOCK_Connector,
+ "$Id$")
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+ACE_ALLOC_HOOK_DEFINE(ACE_SSL_SOCK_Connector)
+
+ACE_SSL_SOCK_Connector::~ACE_SSL_SOCK_Connector (void)
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Connector::~ACE_SSL_SOCK_Connector");
+}
+
+int
+ACE_SSL_SOCK_Connector::ssl_connect (ACE_SSL_SOCK_Stream &new_stream,
+ const ACE_Time_Value *timeout)
+{
+ SSL *ssl = new_stream.ssl ();
+
+ if (SSL_is_init_finished (ssl))
+ return 0;
+
+ // Check if a connection is already pending for the given SSL
+ // structure.
+ if (!SSL_in_connect_init (ssl))
+ ::SSL_set_connect_state (ssl);
+
+ ACE_HANDLE handle = new_stream.get_handle ();
+
+ // We're going to call SSL_connect, optionally doing ACE::select and
+ // retrying the SSL_connect, until the SSL handshake is done or
+ // it fails.
+ // To get the timeout affect, set the socket to nonblocking mode
+ // before beginning if there is a timeout specified. If the timeout
+ // is 0 (wait as long as it takes) then don't worry about the blocking
+ // status; we'll block in SSL_connect if the socket is blocking, and
+ // block in ACE::select if not.
+ int reset_blocking_mode = 0;
+ if (timeout != 0)
+ {
+ reset_blocking_mode = ACE_BIT_DISABLED (ACE::get_flags (handle),
+ ACE_NONBLOCK);
+ // Set the handle into non-blocking mode if it's not already
+ // in it.
+ if (reset_blocking_mode
+ && ACE::set_flags (handle,
+ ACE_NONBLOCK) == -1)
+ return -1;
+ }
+
+ ACE_Time_Value t;
+ if (timeout != 0)
+ t = *timeout; // Need a non-const copy.
+
+ // Take into account the time between each select() call below.
+ ACE_Countdown_Time countdown ((timeout == 0 ? 0 : &t));
+
+ int status;
+ do
+ {
+ // These handle sets are used to set up for whatever SSL_connect
+ // says it wants next. They're reset on each pass around the loop.
+ ACE_Handle_Set rd_handle;
+ ACE_Handle_Set wr_handle;
+
+ status = ::SSL_connect (ssl);
+ switch (::SSL_get_error (ssl, status))
+ {
+ case SSL_ERROR_NONE:
+ // Start out with non-blocking disabled on the SSL stream.
+ new_stream.disable (ACE_NONBLOCK);
+ status = 0; // To tell caller about success
+ break; // Done
+
+ case SSL_ERROR_WANT_WRITE:
+ wr_handle.set_bit (handle);
+ status = 1; // Wait for more activity
+ break;
+
+ case SSL_ERROR_WANT_READ:
+ rd_handle.set_bit (handle);
+ status = 1; // Wait for more activity
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ // The peer has notified us that it is shutting down via
+ // the SSL "close_notify" message so we need to
+ // shutdown, too.
+ status = -1;
+ break;
+
+ case SSL_ERROR_SYSCALL:
+ // On some platforms (e.g. MS Windows) OpenSSL does not
+ // store the last error in errno so explicitly do so.
+ //
+ // Explicitly check for EWOULDBLOCK since it doesn't get
+ // converted to an SSL_ERROR_WANT_{READ,WRITE} on some
+ // platforms. If SSL_connect failed outright, though, don't
+ // bother checking more. This can happen if the socket gets
+ // closed during the handshake.
+ if (ACE_OS::set_errno_to_last_error () == EWOULDBLOCK &&
+ status == -1)
+ {
+ // Although the SSL_ERROR_WANT_READ/WRITE isn't getting
+ // set correctly, the read/write state should be valid.
+ // Use that to decide what to do.
+ status = 1; // Wait for more activity
+ if (SSL_want_write (ssl))
+ wr_handle.set_bit (handle);
+ else if (SSL_want_read (ssl))
+ rd_handle.set_bit (handle);
+ else
+ status = -1; // Doesn't want anything - bail out
+ }
+ else
+ status = -1;
+ break;
+
+ default:
+ ACE_SSL_Context::report_error ();
+ status = -1;
+ break;
+ }
+
+ if (status == 1)
+ {
+ // Must have at least one handle to wait for at this point.
+ ACE_ASSERT (rd_handle.num_set () == 1 || wr_handle.num_set () == 1);
+
+ // Block indefinitely if timeout pointer is zero.
+ status = ACE::select (int (handle) + 1,
+ &rd_handle,
+ &wr_handle,
+ 0,
+ (timeout == 0 ? 0 : &t));
+
+ (void) countdown.update ();
+
+ // 0 is timeout, so we're done.
+ // -1 is error, so we're done.
+ // Could be both handles set (same handle in both masks) so set to 1.
+ if (status >= 1)
+ status = 1;
+ else // Timeout or socket failure
+ status = -1;
+ }
+
+ } while (status == 1 && !SSL_is_init_finished (ssl));
+
+ if (reset_blocking_mode)
+ {
+ ACE_Errno_Guard eguard (errno);
+ ACE::clr_flags (handle, ACE_NONBLOCK);
+ }
+
+ return (status == -1 ? -1 : 0);
+
+}
+
+int
+ACE_SSL_SOCK_Connector::connect (ACE_SSL_SOCK_Stream &new_stream,
+ const ACE_Addr &remote_sap,
+ const ACE_Time_Value *timeout,
+ const ACE_Addr &local_sap,
+ int reuse_addr,
+ int flags,
+ int perms)
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Connector::connect");
+
+ // Take into account the time to complete the basic TCP handshake
+ // and the SSL handshake.
+ ACE_Time_Value time_copy;
+ ACE_Countdown_Time countdown (&time_copy);
+ if (timeout != 0)
+ {
+ time_copy += *timeout;
+ countdown.start ();
+ }
+
+ int result =
+ this->connector_.connect (new_stream.peer (),
+ remote_sap,
+ timeout,
+ local_sap,
+ reuse_addr,
+ flags,
+ perms);
+
+ int error = 0;
+ if (result == -1)
+ error = errno; // Save us some TSS accesses.
+
+ // Obtain the handle from the underlying SOCK_Stream and set it in
+ // the SSL_SOCK_Stream. Note that the case where a connection is in
+ // progress is also handled. In that case, the handle must also be
+ // set in the SSL_SOCK_Stream so that the correct handle is returned
+ // when performing non-blocking connect()s.
+ if (new_stream.get_handle () == ACE_INVALID_HANDLE
+ && (result == 0
+ || (result == -1 && (error == EWOULDBLOCK
+ || error == EINPROGRESS))))
+ new_stream.set_handle (new_stream.peer ().get_handle ());
+
+ if (result == -1)
+ return result;
+
+ // If using a timeout, update the countdown timer to reflect the time
+ // spent on the connect itself, then pass the remaining time to
+ // ssl_connect to bound the time on the handshake.
+ if (timeout != 0)
+ {
+ countdown.update ();
+ timeout = &time_copy;
+ }
+
+ result = this->ssl_connect (new_stream, timeout);
+
+ if (result == -1)
+ new_stream.close ();
+
+ return result;
+}
+
+int
+ACE_SSL_SOCK_Connector::connect (ACE_SSL_SOCK_Stream &new_stream,
+ const ACE_Addr &remote_sap,
+ ACE_QoS_Params qos_params,
+ const ACE_Time_Value *timeout,
+ const ACE_Addr &local_sap,
+ ACE_Protocol_Info *protocolinfo,
+ ACE_SOCK_GROUP g,
+ u_long flags,
+ int reuse_addr,
+ int perms)
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Connector::connect");
+
+ // Take into account the time to complete the basic TCP handshake
+ // and the SSL handshake.
+ ACE_Time_Value time_copy;
+ ACE_Countdown_Time countdown (&time_copy);
+ if (timeout != 0)
+ {
+ time_copy += *timeout;
+ countdown.start ();
+ }
+
+ int result = this->connector_.connect (new_stream.peer (),
+ remote_sap,
+ qos_params,
+ timeout,
+ local_sap,
+ protocolinfo,
+ g,
+ flags,
+ reuse_addr,
+ perms);
+
+ int error = 0;
+ if (result == -1)
+ error = errno; // Save us some TSS accesses.
+
+ // Obtain the handle from the underlying SOCK_Stream and set it in
+ // the SSL_SOCK_Stream. Note that the case where a connection is in
+ // progress is also handled. In that case, the handle must also be
+ // set in the SSL_SOCK_Stream so that the correct handle is returned
+ // when performing non-blocking connect()s.
+ if (new_stream.get_handle () == ACE_INVALID_HANDLE
+ && (result == 0
+ || (result == -1 && (error == EWOULDBLOCK
+ || error == EINPROGRESS))))
+ new_stream.set_handle (new_stream.peer ().get_handle ());
+
+ if (result == -1)
+ return result;
+
+ // If using a timeout, update the countdown timer to reflect the time
+ // spent on the connect itself, then pass the remaining time to
+ // ssl_connect to bound the time on the handshake.
+ if (timeout != 0)
+ {
+ countdown.update ();
+ timeout = &time_copy;
+ }
+
+ result = this->ssl_connect (new_stream, timeout);
+
+ if (result == -1)
+ new_stream.close ();
+
+ return result;
+}
+
+// Try to complete a non-blocking connection.
+
+int
+ACE_SSL_SOCK_Connector::complete (ACE_SSL_SOCK_Stream &new_stream,
+ ACE_Addr *remote_sap,
+ const ACE_Time_Value *tv)
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Connector::complete");
+
+ // Take into account the time to complete the basic TCP handshake
+ // and the SSL handshake.
+ ACE_Time_Value time_copy;
+ ACE_Countdown_Time countdown (&time_copy);
+ if (tv != 0)
+ {
+ time_copy += *tv;
+ countdown.start ();
+ }
+
+ // Only attempt to complete the TCP connection if it that hasn't
+ // already been done.
+ ACE_INET_Addr raddr;
+ if (new_stream.peer ().get_remote_addr (raddr) != 0
+ && this->connector_.complete (new_stream.peer (),
+ remote_sap,
+ tv) == -1)
+ return -1;
+
+ // The handle in the SSL_SOCK_Stream should have already been set in
+ // the connect() method.
+
+ // If using a timeout, update the countdown timer to reflect the time
+ // spent on the connect itself, then pass the remaining time to
+ // ssl_connect to bound the time on the handshake.
+ if (tv != 0)
+ {
+ countdown.update ();
+ tv = &time_copy;
+ }
+
+ if (this->ssl_connect (new_stream, tv) == -1)
+ {
+ new_stream.close ();
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector (
+ ACE_SSL_SOCK_Stream &new_stream,
+ const ACE_Addr &remote_sap,
+ const ACE_Time_Value *timeout,
+ const ACE_Addr &local_sap,
+ int reuse_addr,
+ int flags,
+ int perms)
+ : connector_ ()
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector");
+ this->connect (new_stream,
+ remote_sap,
+ timeout,
+ local_sap,
+ reuse_addr,
+ flags,
+ perms);
+}
+
+ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector (
+ ACE_SSL_SOCK_Stream &new_stream,
+ const ACE_Addr &remote_sap,
+ ACE_QoS_Params qos_params,
+ const ACE_Time_Value *timeout,
+ const ACE_Addr &local_sap,
+ ACE_Protocol_Info *protocolinfo,
+ ACE_SOCK_GROUP g,
+ u_long flags,
+ int reuse_addr,
+ int perms)
+ : connector_ ()
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Connector::ACE_SSL_SOCK_Connector");
+
+ this->connect (new_stream,
+ remote_sap,
+ qos_params,
+ timeout,
+ local_sap,
+ protocolinfo,
+ g,
+ flags,
+ reuse_addr,
+ perms);
+}
+
+ACE_END_VERSIONED_NAMESPACE_DECL