diff options
Diffstat (limited to 'ACE/ace/SSL/SSL_SOCK_Stream.inl')
-rw-r--r-- | ACE/ace/SSL/SSL_SOCK_Stream.inl | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/ACE/ace/SSL/SSL_SOCK_Stream.inl b/ACE/ace/SSL/SSL_SOCK_Stream.inl new file mode 100644 index 00000000000..a720f07ebc6 --- /dev/null +++ b/ACE/ace/SSL/SSL_SOCK_Stream.inl @@ -0,0 +1,369 @@ +// -*- C++ -*- +// +// $Id$ + +#include "ace/OS_NS_errno.h" +#include "ace/Truncate.h" + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +ACE_INLINE void +ACE_SSL_SOCK_Stream::set_handle (ACE_HANDLE fd) +{ + if (this->ssl_ == 0 || fd == ACE_INVALID_HANDLE) + { + this->ACE_SSL_SOCK::set_handle (ACE_INVALID_HANDLE); + return; + } + else + { + (void) ::SSL_set_fd (this->ssl_, (int) fd); + this->ACE_SSL_SOCK::set_handle (fd); + this->stream_.set_handle (fd); + } +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send_i (const void *buf, + size_t n, + int flags) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_i"); + + // NOTE: Caller must provide thread-synchronization. + + // No send flags are supported in SSL. + if (flags != 0) + { + ACE_NOTSUP_RETURN (-1); + } + + int const bytes_sent = ::SSL_write (this->ssl_, + static_cast<const char *> (buf), + ACE_Utils::truncate_cast<int> (n)); + + switch (::SSL_get_error (this->ssl_, bytes_sent)) + { + case SSL_ERROR_NONE: + return bytes_sent; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EWOULDBLOCK; + + return -1; + + 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. + (void) ::SSL_shutdown (this->ssl_); + + return bytes_sent; + + case SSL_ERROR_SYSCALL: + if (bytes_sent == 0) + // An EOF occured but the SSL "close_notify" message was not + // sent. This is a protocol error, but we ignore it. + return 0; + + // If not an EOF, then fall through to "default" case. + + // On some platforms (e.g. MS Windows) OpenSSL does not store + // the last error in errno so explicitly do so. + ACE_OS::set_errno_to_last_error (); + + break; + + default: + // Reset errno to prevent previous values (e.g. EWOULDBLOCK) + // from being associated with fatal SSL errors. + errno = 0; + + ACE_SSL_Context::report_error (); + + break; + } + + return -1; +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t n, + int flags) const +{ + return this->send_i (buf, n, flags); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv_i (void *buf, + size_t n, + int flags, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_i"); + + // NOTE: Caller must provide thread-synchronization. + + int bytes_read = 0; + ACE_HANDLE const handle = this->get_handle (); + + // Value for current I/O mode (blocking/non-blocking) + int val = 0; + + if (timeout != 0) + ACE::record_and_set_non_blocking_mode (handle, + val); + + // Only block to wait for I/O ready with a timeout if all on-hand data + // has been consumed. If there is remaining data in the SSL buffers + // the socket won't "select" even though SSL_read would complete. + // See "SSL and TLS" by Rescorla section 8.9 for more info. + bool peeking = false; + bool retry = false; + if (flags) + { + if (ACE_BIT_ENABLED (flags, MSG_PEEK)) + { + peeking = true; + } + else + { + ACE_NOTSUP_RETURN (-1); + } + } + + do + { + retry = false; + if (peeking) + { + bytes_read = ::SSL_peek (this->ssl_, + static_cast<char *> (buf), + ACE_Utils::truncate_cast<int> (n)); + } + else + { + bytes_read = ::SSL_read (this->ssl_, + static_cast<char *> (buf), + ACE_Utils::truncate_cast<int> (n)); + } + + int const status = ::SSL_get_error (this->ssl_, bytes_read); + int substat = 0; + switch (status) + { + case SSL_ERROR_NONE: + break; + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + if (timeout == 0) + { + errno = EWOULDBLOCK; + bytes_read = -1; + break; + } + substat = ACE::handle_ready (handle, + timeout, + status == SSL_ERROR_WANT_READ, + status == SSL_ERROR_WANT_WRITE, + 0); + if (substat == 1) // Now ready to proceed + { + retry = true; + break; + } + bytes_read = -1; + if (substat == 0) + errno = ETIME; + break; + + case SSL_ERROR_ZERO_RETURN: + bytes_read = 0; + + // The peer has notified us that it is shutting down via the SSL + // "close_notify" message so we need to shutdown, too. + (void) ::SSL_shutdown (this->ssl_); + + break; + + case SSL_ERROR_SYSCALL: + if (bytes_read == 0) + // An EOF occured but the SSL "close_notify" message was not + // sent. This is a protocol error, but we ignore it. + break; + + // If not an EOF, then fall through to "default" case. + + // On some platforms (e.g. MS Windows) OpenSSL does not store + // the last error in errno so explicitly do so. + ACE_OS::set_errno_to_last_error (); + + break; + + default: + // Reset errno to prevent previous values (e.g. EWOULDBLOCK) + // from being associated with a fatal SSL error. + bytes_read = -1; + errno = 0; + + ACE_SSL_Context::report_error (); + + break; + } + } while (retry); + + if (timeout != 0) + ACE::restore_non_blocking_mode (handle, val); + return bytes_read; +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n, + int flags) const +{ + return this->recv_i (buf, n, flags, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t n) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send"); + + return this->send_i (buf, n, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv"); + + return this->recv_i (buf, n, 0, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send (const void *buf, + size_t len, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send"); + return this->send (buf, len, 0, timeout); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv (void *buf, + size_t n, + const ACE_Time_Value *timeout) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv"); + return this->recv (buf, n, 0, timeout); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv_n (void *buf, int buf_size) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n"); + return this->recv_n (buf, buf_size, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::recv_n (void *buf, + size_t len, + const ACE_Time_Value *timeout, + size_t *bytes_transferred) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::recv_n"); + return this->recv_n (buf, len, 0, timeout, bytes_transferred); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send_n (const void *buf, int len) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n"); + return this->send_n (buf, len, 0); +} + +ACE_INLINE ssize_t +ACE_SSL_SOCK_Stream::send_n (const void *buf, + size_t len, + const ACE_Time_Value *timeout, + size_t *bytes_transferred) const +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::send_n"); + return this->send_n (buf, len, 0, timeout, bytes_transferred); +} + +ACE_INLINE int +ACE_SSL_SOCK_Stream::close_reader (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::close_reader"); + return this->stream_.close_reader (); +} + +ACE_INLINE int +ACE_SSL_SOCK_Stream::close_writer (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::close_writer"); + return this->stream_.close_writer (); +} + +ACE_INLINE int +ACE_SSL_SOCK_Stream::close (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::close"); + + if (this->ssl_ == 0 || this->get_handle () == ACE_INVALID_HANDLE) + return 0; // SSL_SOCK_Stream was never opened. + + // SSL_shutdown() returns 1 on successful shutdown of the SSL + // connection, not 0. + int const status = ::SSL_shutdown (this->ssl_); + + switch (::SSL_get_error (this->ssl_, status)) + { + case SSL_ERROR_NONE: + case SSL_ERROR_SYSCALL: // Ignore this error condition. + + // Reset the SSL object to allow another connection to be made + // using this ACE_SSL_SOCK_Stream instance. This prevents the + // previous SSL session state from being associated with the new + // SSL session/connection. + (void) ::SSL_clear (this->ssl_); + this->set_handle (ACE_INVALID_HANDLE); + return this->stream_.close (); + + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EWOULDBLOCK; + break; + + default: + ACE_SSL_Context::report_error (); + + ACE_Errno_Guard error (errno); // Save/restore errno + (void) this->stream_.close (); + + return -1; + } + + return -1; +} + +ACE_INLINE ACE_SOCK_Stream & +ACE_SSL_SOCK_Stream::peer (void) +{ + ACE_TRACE ("ACE_SSL_SOCK_Stream::peer"); + return this->stream_; +} + +ACE_INLINE SSL * +ACE_SSL_SOCK_Stream::ssl (void) const +{ + return this->ssl_; +} + +ACE_END_VERSIONED_NAMESPACE_DECL |