summaryrefslogtreecommitdiff
path: root/ACE/ace/SSL/SSL_SOCK_Stream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/SSL/SSL_SOCK_Stream.cpp')
-rw-r--r--ACE/ace/SSL/SSL_SOCK_Stream.cpp586
1 files changed, 586 insertions, 0 deletions
diff --git a/ACE/ace/SSL/SSL_SOCK_Stream.cpp b/ACE/ace/SSL/SSL_SOCK_Stream.cpp
new file mode 100644
index 00000000000..2b34fbaca02
--- /dev/null
+++ b/ACE/ace/SSL/SSL_SOCK_Stream.cpp
@@ -0,0 +1,586 @@
+// $Id$
+
+#include "ace/Handle_Set.h"
+#include "ace/Log_Msg.h"
+#include "ace/Countdown_Time.h"
+#include "ace/OS_NS_string.h"
+#include "ace/OS_NS_sys_select.h"
+#include "ace/OS_Memory.h"
+
+#include <openssl/err.h>
+
+#include "SSL_SOCK_Stream.h"
+
+#if !defined (__ACE_INLINE__)
+#include "SSL_SOCK_Stream.inl"
+#endif /* __ACE_INLINE__ */
+
+ACE_RCSID (ACE_SSL,
+ SSL_SOCK_Stream,
+ "$Id$")
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+ACE_ALLOC_HOOK_DEFINE(ACE_SSL_SOCK_Stream)
+
+ACE_SSL_SOCK_Stream::ACE_SSL_SOCK_Stream (ACE_SSL_Context *context)
+ : ssl_ (0),
+ stream_ ()
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::ACE_SSL_SOCK_Stream");
+
+ ACE_SSL_Context * ctx =
+ (context == 0 ? ACE_SSL_Context::instance () : context);
+
+ this->ssl_ = ::SSL_new (ctx->context ());
+
+ if (this->ssl_ == 0)
+ {
+ ACE_ERROR ((LM_ERROR,
+ "(%P|%t) ACE_SSL_SOCK_Stream "
+ "- cannot allocate new SSL structure %p\n",
+ ACE_TEXT ("")));
+ }
+}
+
+ACE_SSL_SOCK_Stream::~ACE_SSL_SOCK_Stream (void)
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::~ACE_SSL_SOCK_Stream");
+
+ ::SSL_free (this->ssl_);
+
+ // @@ Question: should we reference count the Context object or
+ // leave that to the application developer? We do not reference
+ // count reactors (for example) and following some simple rules
+ // seems to work fine!
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::sendv (const iovec iov[],
+ size_t n,
+ const ACE_Time_Value *max_wait_time) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::sendv");
+
+ // There is subtle problem in this method that occurs when using
+ // non-blocking IO. The semantics of a non-blocking scatter write
+ // (sendv()) are not possible to retain with the emulation in this
+ // method.
+
+ ssize_t bytes_sent = 0;
+
+ ACE_Time_Value t;
+ ACE_Time_Value *timeout =
+ const_cast<ACE_Time_Value *> (max_wait_time);
+
+ if (max_wait_time != 0)
+ {
+ // Make a copy since ACE_Countdown_Time modifies the
+ // ACE_Time_Value.
+ t = *max_wait_time;
+ timeout = &t;
+ }
+
+ // Take into account the time between each send.
+ ACE_Countdown_Time countdown (timeout);
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ const ssize_t result = this->send (iov[i].iov_base,
+ iov[i].iov_len,
+ timeout);
+
+ if (result == -1)
+ {
+ // There is a subtle difference in behaviour depending on
+ // whether or not any data was sent. If no data was sent,
+ // then always return -1. Otherwise return bytes_sent.
+ // This gives the caller an opportunity to keep track of
+ if (bytes_sent > 0)
+ break;
+ else
+ return -1;
+ }
+ else
+ {
+ bytes_sent += result;
+
+ // Do not continue on to the next loop iteration if the
+ // amount of data sent was less than the amount data given.
+ // This avoids a subtle problem where "holes" in the data
+ // stream would occur if partial sends of a given buffer in
+ // the iovec array occured.
+ if (static_cast<size_t> (result) < iov[i].iov_len)
+ break;
+ }
+
+ (void) countdown.update ();
+ }
+
+ return bytes_sent;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::recvv (iovec *io_vec,
+ const ACE_Time_Value *timeout) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::recvv");
+
+ // From ACE_SOCK_IO::recvv().
+#if defined (FIONREAD)
+ ACE_Handle_Set handle_set;
+ handle_set.reset ();
+ handle_set.set_bit (this->get_handle ());
+
+ io_vec->iov_base = 0;
+
+ // Check the status of the current socket.
+ switch (ACE_OS::select (int (this->get_handle ()) + 1,
+ handle_set,
+ 0, 0,
+ timeout))
+ {
+ case -1:
+ return -1;
+ /* NOTREACHED */
+ case 0:
+ errno = ETIME;
+ return -1;
+ /* NOTREACHED */
+ default:
+ // Goes fine, fallthrough to get data
+ break;
+ }
+
+ int inlen;
+
+ if (ACE_OS::ioctl (this->get_handle (),
+ FIONREAD,
+ &inlen) == -1)
+ return -1;
+ else if (inlen > 0)
+ {
+ ACE_NEW_RETURN (io_vec->iov_base,
+ char[inlen],
+ -1);
+ io_vec->iov_len = this->recv (io_vec->iov_base,
+ inlen);
+ return io_vec->iov_len;
+ }
+ else
+ return 0;
+#else
+ ACE_UNUSED_ARG (io_vec);
+ ACE_UNUSED_ARG (timeout);
+ ACE_NOTSUP_RETURN (-1);
+#endif /* FIONREAD */
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::send (const void *buf,
+ size_t len,
+ int flags,
+ const ACE_Time_Value *timeout) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::send");
+
+ // If SSL has data in the buffer, i.e. SSL_pending() returns a
+ // non-zero value, then don't block on select().
+ if (timeout == 0 || ::SSL_pending (this->ssl_))
+ return this->send (buf, len, flags);
+
+ int val = 0;
+ if (ACE::enter_send_timedwait (this->get_handle (),
+ timeout,
+ val) == -1)
+ return -1;
+
+ ssize_t bytes_transferred = this->send (buf, len, flags);
+
+ ACE::restore_non_blocking_mode (this->get_handle (), val);
+
+ return bytes_transferred;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::recv (void *buf,
+ size_t n,
+ int flags,
+ const ACE_Time_Value *timeout) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::recv");
+
+ return this->recv_i (buf, n, flags, timeout);
+}
+
+
+ssize_t
+ACE_SSL_SOCK_Stream::send (size_t n, ...) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::send");
+
+ const size_t total_tuples = n / 2;
+
+ va_list argp;
+ va_start (argp, n);
+
+ ssize_t bytes_sent = 0;
+
+ // NOTE: This method used to fill an IO vector (e.g. iovec) and then
+ // send it using a scatter write (sendv()). However, it is
+ // not possible to emulate a non-blocking scatter write over
+ // SSL. As such, there is no point in attempting to use
+ // scatter writes over SSL.
+ for (size_t i = 0; i < total_tuples; ++i)
+ {
+ const ssize_t data_len = va_arg (argp, ssize_t);
+ const ssize_t result = this->send (va_arg (argp, char *),
+ data_len);
+
+ if (result == -1)
+ {
+ // There is a subtle difference in behaviour depending on
+ // whether or not any data was sent. If no data was sent,
+ // then always return -1. Otherwise return bytes_sent.
+ // This gives the caller an opportunity to keep track of
+ // which data was actually sent.
+ if (bytes_sent > 0)
+ break;
+ else
+ {
+ va_end (argp);
+ return -1;
+ }
+ }
+ else
+ {
+ bytes_sent += result;
+
+ // Do not continue on to the next loop iteration if the
+ // amount of data sent was less than the amount of data
+ // given. This avoids a subtle problem where "holes" in the
+ // data stream would occur if partial sends of a given
+ // buffer in the varargs occured.
+ if (result < data_len)
+ break;
+
+ }
+ }
+
+ va_end (argp);
+
+ return bytes_sent;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::recv (size_t n, ...) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::recv");
+
+ const size_t total_tuples = n / 2;
+
+ va_list argp;
+ va_start (argp, n);
+
+ ssize_t bytes_recv = 0;
+
+ for (size_t i = 0; i < total_tuples; ++i)
+ {
+ const ssize_t data_len = va_arg (argp, ssize_t);
+ const ssize_t result = this->recv (va_arg (argp, char *),
+ data_len);
+
+ if (result == -1)
+ {
+ // There is a subtle difference in behaviour depending on
+ // whether or not any data was received. If no data was
+ // received, then always return -1. Otherwise return
+ // bytes_received. This gives the caller an opportunity to
+ // keep track of which data was actually received.
+ if (bytes_recv > 0)
+ break;
+ else
+ {
+ va_end (argp);
+ return -1;
+ }
+ }
+ else
+ {
+ bytes_recv += result;
+
+ // Do not continue on to the next loop iteration if the
+ // amount of data received was less than the amount of data
+ // desired. This avoids a subtle problem where "holes" in
+ // the data stream would occur if partial receives of a
+ // given buffer in the varargs occured.
+ if (result < data_len)
+ break;
+
+ }
+ }
+
+ va_end (argp);
+
+ return bytes_recv;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::send_n (const void *buf,
+ size_t len,
+ int flags,
+ const ACE_Time_Value *timeout,
+ size_t *bt) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n");
+
+ // No support for send flags in SSL.
+ if (flags != 0)
+ ACE_NOTSUP_RETURN (-1);
+
+ /* This code mimics ACE::send_n */
+ // Total number of bytes written.
+ size_t temp = 0;
+ size_t &bytes_transferred = ((bt == 0) ? temp : *bt);
+
+ // Actual number of bytes written in each <send> attempt
+ ssize_t n = 0;
+
+ for (bytes_transferred = 0;
+ bytes_transferred < len;
+ bytes_transferred += n)
+ {
+ n = this->send ((const char*) buf + bytes_transferred,
+ len - bytes_transferred,
+ flags,
+ timeout);
+
+ if (n < 0)
+ {
+ if (errno == EWOULDBLOCK)
+ {
+ // If blocked, try again.
+ n = 0;
+ continue;
+ }
+ else
+ return -1;
+ }
+ else if (n == 0)
+ break;
+ }
+
+ return bytes_transferred;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::recv_n (void *buf,
+ size_t len,
+ int flags,
+ const ACE_Time_Value *timeout,
+ size_t *bt) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n");
+
+ if (flags != 0)
+ {
+ if ((flags | MSG_PEEK) != MSG_PEEK)
+ ACE_NOTSUP_RETURN (-1);
+ }
+
+ size_t temp = 0;
+ size_t &bytes_transferred = ((bt == 0) ? temp : *bt);
+
+ ssize_t n = 0;
+
+ for (bytes_transferred = 0;
+ bytes_transferred < len;
+ bytes_transferred += n)
+ {
+ n = this->recv ((char*) buf + bytes_transferred,
+ len - bytes_transferred,
+ flags,
+ timeout);
+
+ if (n < 0)
+ {
+ if (errno == EWOULDBLOCK)
+ {
+ // If blocked, try again.
+ n = 0;
+ continue;
+ }
+ else
+ return -1;
+ }
+ else if (n == 0)
+ break;
+ }
+
+ return bytes_transferred;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::recv_n (void *buf, int len, int flags) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n");
+
+ if (flags != 0)
+ {
+ if ((flags | MSG_PEEK) != MSG_PEEK)
+ ACE_NOTSUP_RETURN (-1);
+ }
+
+ ssize_t bytes_transferred = 0;
+ ssize_t n = 0;
+
+ for (bytes_transferred = 0;
+ bytes_transferred < len;
+ bytes_transferred += n)
+ {
+ n = this->recv ((char*) buf + bytes_transferred,
+ len - bytes_transferred,
+ flags);
+
+ if (n < 0)
+ {
+ if (errno == EWOULDBLOCK)
+ {
+ // If blocked, try again.
+ n = 0;
+ continue;
+ }
+ else
+ return -1;
+ }
+ else if (n == 0)
+ break;
+ }
+
+ return bytes_transferred;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::send_n (const void *buf, int len, int flags) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n");
+
+ // Send flags are unsupported in SSL
+ if (flags != 0)
+ ACE_NOTSUP_RETURN (-1);
+
+ /* The following code mimics <ACE::send_n> */
+ size_t bytes_transferred = 0;
+ ssize_t n = 0;
+
+ for (bytes_transferred = 0;
+ bytes_transferred < (size_t) len;
+ bytes_transferred += n)
+ {
+ n = this->send ((const char*) buf + bytes_transferred,
+ len - bytes_transferred,
+ flags);
+
+ if (n < 0)
+ {
+ if (errno == EWOULDBLOCK)
+ {
+ // If blocked, try again.
+ n = 0;
+ continue;
+ }
+ else
+ return -1;
+ }
+ else if (n == 0)
+ break;
+ }
+
+ return bytes_transferred;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::sendv_n (const iovec iov[], size_t iovcnt) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::sendv_n");
+
+ ssize_t bytes_sent = 0;
+
+ for (size_t i = 0; i < iovcnt; ++i)
+ {
+ ssize_t result = this->send_n (iov[i].iov_base,
+ iov[i].iov_len);
+
+
+ if (result == -1)
+ {
+ // There is a subtle difference in behaviour depending on
+ // whether or not any data was sent. If no data was sent,
+ // then always return -1. Otherwise return bytes_sent.
+ // This gives the caller an opportunity to keep track of
+ // which data was actually sent.
+ if (bytes_sent > 0)
+ break;
+ else
+ return -1;
+ }
+ else
+ bytes_sent += result;
+ }
+
+ return bytes_sent;
+}
+
+ssize_t
+ACE_SSL_SOCK_Stream::recvv_n (iovec iov[], size_t iovcnt) const
+{
+ ACE_TRACE ("ACE_SSL_SOCK_Stream::recvv_n");
+
+ ssize_t bytes_read = 0;
+
+ for (size_t i = 0; i < iovcnt; ++i)
+ {
+ ssize_t result = this->recv_n (iov[i].iov_base,
+ iov[i].iov_len);
+
+ if (result == -1)
+ {
+ // There is a subtle difference in behaviour depending on
+ // whether or not any data was read. If no data was read,
+ // then always return -1. Otherwise return bytes_read.
+ // This gives the caller an opportunity to keep track of
+ // which data was actually read.
+ if (bytes_read > 0)
+ break;
+ else
+ return -1;
+ }
+ else
+ bytes_read += result;
+ }
+
+ return bytes_read;
+}
+
+int
+ACE_SSL_SOCK_Stream::get_remote_addr (ACE_Addr &addr) const
+{
+ // Some applications use get_remote_addr() as a way of determining
+ // whether or not a connection has been established. In SSL's case,
+ // the remote addr will be available once the TCP handshake has been
+ // complete. Despite that fact, the SSL connection may not have
+ // been completed. In such a case, a successful return from
+ // get_remote_addr() would be misleading.
+
+ if (SSL_is_init_finished (this->ssl_))
+ return this->ACE_SSL_SOCK::get_remote_addr (addr);
+
+ if (this->get_handle () == ACE_INVALID_HANDLE)
+ errno = EBADF;
+ else
+ errno = ENOTCONN;
+
+ return -1;
+}
+
+ACE_END_VERSIONED_NAMESPACE_DECL