diff options
Diffstat (limited to 'ACE/ace/SSL/SSL_SOCK_Stream.cpp')
-rw-r--r-- | ACE/ace/SSL/SSL_SOCK_Stream.cpp | 586 |
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..096528e543b --- /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.i" +#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 |