server/dep/ACE_wrappers/ace/SSL/SSL_SOCK_Stream.cpp
cipherCOM 571f510ee4 [11162] Changed ACE lib to same version but with configure script
(based on cipherCOM's repo commit d3d8934)

Signed-off-by: VladimirMangos <vladimir@getmangos.com>
2011-02-14 12:51:16 +03:00

626 lines
15 KiB
C++

// $Id: SSL_SOCK_Stream.cpp 91368 2010-08-16 13:03:34Z mhengstmengel $
#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_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)
{
ssize_t const 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) < static_cast<size_t> (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 const 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");
size_t const 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)
{
ssize_t const data_len = va_arg (argp, ssize_t);
ssize_t const 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");
size_t const 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)
{
ssize_t const data_len = va_arg (argp, ssize_t);
ssize_t const 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 ACE_Utils::truncate_cast<ssize_t> (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 ACE_Utils::truncate_cast<ssize_t> (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 ACE_Utils::truncate_cast<ssize_t> (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 ACE_Utils::truncate_cast<ssize_t> (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 const 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