// $Id$ #include "ace/Handle_Set.h" #include "ace/Log_Msg.h" #include "ace/Time_Value.h" #include "ace/OS_NS_string.h" #include "ace/OS_NS_sys_select.h" #include #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_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) { ::SSL_set_verify (this->ssl_, ctx->default_verify_mode (), 0); } else { 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 (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 (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; } u_long inlen; if (ACE_OS::ioctl (this->get_handle (), FIONREAD, (u_long *) &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 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 */ 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; }