static int amqp_openssl_bio_should_retry(int res) {
  if (res == -1) {
    int err = amqp_os_socket_error();
    if (
#ifdef EWOULDBLOCK
        err == EWOULDBLOCK ||
#endif
#ifdef WSAEWOULDBLOCK
        err == WSAEWOULDBLOCK ||
#endif
#ifdef ENOTCONN
        err == ENOTCONN ||
#endif
#ifdef EINTR
        err == EINTR ||
#endif
#ifdef EAGAIN
        err == EAGAIN ||
#endif
#ifdef EPROTO
        err == EPROTO ||
#endif
#ifdef EINPROGRESS
        err == EINPROGRESS ||
#endif
#ifdef EALREADY
        err == EALREADY ||
#endif
        0) {
      return 1;
    }
  }
  return 0;
}
Exemple #2
0
static ssize_t
amqp_tcp_socket_send(void *base, const void *buf, size_t len)
{
    struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
    ssize_t res;
    int flags = 0;

    if (-1 == self->sockfd) {
        return AMQP_STATUS_SOCKET_CLOSED;
    }

#ifdef MSG_NOSIGNAL
    flags |= MSG_NOSIGNAL;
#endif

start:
#ifdef _WIN32
    res = send(self->sockfd, buf, (int)len, flags);
#else
    res = send(self->sockfd, buf, len, flags);
#endif

    if (res < 0) {
        self->internal_error = amqp_os_socket_error();
        switch (self->internal_error) {
        case EINTR:
            goto start;
        case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
        case EAGAIN:
#endif
            res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
            break;
        default:
            res = AMQP_STATUS_SOCKET_ERROR;
        }
    } else {
        self->internal_error = 0;
    }

    return res;
}
static ssize_t
amqp_tcp_socket_recv(void *base, void *buf, size_t len, int flags)
{
  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
  ssize_t ret;
  if (-1 == self->sockfd) {
    return AMQP_STATUS_SOCKET_CLOSED;
  }

start:
#ifdef _WIN32
  ret = recv(self->sockfd, buf, (int)len, flags);
#else
  ret = recv(self->sockfd, buf, len, flags);
#endif

  if (0 > ret) {
    self->internal_error = amqp_os_socket_error();
    switch (self->internal_error) {
      case EINTR:
        goto start;
#ifdef _WIN32
      case WSAEWOULDBLOCK:
#else
      case EWOULDBLOCK:
#endif
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
      case EAGAIN:
#endif
        ret = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
        break;
      default:
        ret = AMQP_STATUS_SOCKET_ERROR;
    }
  } else if (0 == ret) {
    ret = AMQP_STATUS_CONNECTION_CLOSED;
  }

  return ret;
}
static ssize_t
amqp_tcp_socket_recv(void *base, void *buf, size_t len, int flags)
{
  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
  ssize_t ret;

start:
  ret = recv(self->sockfd, buf, len, flags);

  if (0 > ret) {
    self->internal_error = amqp_os_socket_error();
    if (EINTR == self->internal_error) {
      goto start;
    } else {
      ret = AMQP_STATUS_SOCKET_ERROR;
    }
  } else if (0 == ret) {
    ret = AMQP_STATUS_CONNECTION_CLOSED;
  }

  return ret;
}
static ssize_t
amqp_tcp_socket_send_inner(void *base, const void *buf, size_t len, int flags)
{
  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
  ssize_t res;

  const char *buf_left = buf;
  ssize_t len_left = len;

#ifdef MSG_NOSIGNAL
  flags |= MSG_NOSIGNAL;
#endif

start:
  RABBIT_INFO("Calling send on: %d", self->sockfd);
  res = send(self->sockfd, buf_left, len_left, flags);

  if (res < 0) {
    self->internal_error = amqp_os_socket_error();
    if (EINTR == self->internal_error) {
      goto start;
    } else {
      res = AMQP_STATUS_SOCKET_ERROR;
    }
  } else {
    if (res == len_left) {
      self->internal_error = 0;
      res = AMQP_STATUS_OK;
    } else {
      buf_left += res;
      len_left -= res;
      goto start;
    }
  }

  return res;
}
static ssize_t
amqp_tcp_socket_writev(void *base, struct iovec *iov, int iovcnt)
{
  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
  ssize_t ret;

#if defined(_WIN32)
  DWORD res;
  /* Making the assumption here that WSAsend won't do a partial send
   * unless an error occured, in which case we're hosed so it doesn't matter */
  if (WSASend(self->sockfd, (LPWSABUF)iov, iovcnt, &res, 0, NULL, NULL) == 0) {
    self->internal_error = 0;
    ret = AMQP_STATUS_OK;
  } else {
    self->internal_error = WSAGetLastError();
    ret = AMQP_STATUS_SOCKET_ERROR;
  }
  return ret;

#elif defined(MSG_MORE)
  int i;
  for (i = 0; i < iovcnt - 1; ++i) {
    ret = amqp_tcp_socket_send_inner(self, iov[i].iov_base, iov[i].iov_len, MSG_MORE);
    if (ret != AMQP_STATUS_OK) {
      goto exit;
    }
  }
  ret = amqp_tcp_socket_send_inner(self, iov[i].iov_base, iov[i].iov_len, 0);

exit:
  return ret;

#elif defined(SO_NOSIGPIPE) || !defined(MSG_NOSIGNAL)
  int i;
  ssize_t len_left = 0;


  struct iovec *iov_left = iov;
  int iovcnt_left = iovcnt;

  for (i = 0; i < iovcnt; ++i) {
    len_left += iov[i].iov_len;
  }

start:
  ret = writev(self->sockfd, iov_left, iovcnt_left);

  if (ret < 0) {
    self->internal_error = amqp_os_socket_error();
    if (EINTR == self->internal_error) {
      goto start;
    } else {
      self->internal_error = amqp_os_socket_error();
      ret = AMQP_STATUS_SOCKET_ERROR;
    }
  } else {
    if (ret == len_left) {
      self->internal_error = 0;
      ret = AMQP_STATUS_OK;
    } else {
      len_left -= ret;
      for (i = 0; i < iovcnt_left; ++i) {
        if (ret < (ssize_t)iov_left[i].iov_len) {
          iov_left[i].iov_base = ((char*)iov_left[i].iov_base) + ret;
          iov_left[i].iov_len -= ret;

          iovcnt_left -= i;
          iov_left += i;
          break;
        } else {
          ret -= iov_left[i].iov_len;
        }
      }
      goto start;
    }
  }

  return ret;

#else
  int i;
  size_t bytes = 0;
  void *bufferp;

  for (i = 0; i < iovcnt; ++i) {
    bytes += iov[i].iov_len;
  }

  if (self->buffer_length < bytes) {
    self->buffer = realloc(self->buffer, bytes);
    if (NULL == self->buffer) {
      self->buffer_length = 0;
      self->internal_error = 0;
      ret = AMQP_STATUS_NO_MEMORY;
      goto exit;
    }
    self->buffer_length = bytes;
  }

  bufferp = self->buffer;
  for (i = 0; i < iovcnt; ++i) {
    memcpy(bufferp, iov[i].iov_base, iov[i].iov_len);
    bufferp += iov[i].iov_len;
  }

  ret = amqp_tcp_socket_send_inner(self, self->buffer, bytes, 0);

exit:
  return ret;
#endif
}
static ssize_t
amqp_tcp_socket_send(void *base, const void *buf, size_t len, int flags)
{
  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
  ssize_t res;
  int flagz = 0;

  if (-1 == self->sockfd) {
    return AMQP_STATUS_SOCKET_CLOSED;
  }

#ifdef MSG_NOSIGNAL
  flagz |= MSG_NOSIGNAL;
#endif

#if defined(MSG_MORE)
  if (flags & AMQP_SF_MORE) {
    flagz |= MSG_MORE;
  }
#elif defined(TCP_NOPUSH)
  if (flags & AMQP_SF_MORE && !(self->state & AMQP_SF_MORE)) {
    int one = 1;
    res = setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof(one));
    if (0 != res) {
      self->internal_error = res;
      return AMQP_STATUS_SOCKET_ERROR;
    }
    self->state |= AMQP_SF_MORE;
  } else if (!(flags & AMQP_SF_MORE) && self->state & AMQP_SF_MORE) {
   int zero = 0;
   res = setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &zero, sizeof(&zero));
   if (0 != res) {
     self->internal_error = res;
     res = AMQP_STATUS_SOCKET_ERROR;
   } else {
     self->state &= ~AMQP_SF_MORE;
   }
  }
#endif

start:
#ifdef _WIN32
  res = send(self->sockfd, buf, (int)len, flagz);
#else
  res = send(self->sockfd, buf, len, flagz);
#endif

  if (res < 0) {
    self->internal_error = amqp_os_socket_error();
    switch (self->internal_error) {
      case EINTR:
        goto start;
#ifdef _WIN32
      case WSAEWOULDBLOCK:
#else
      case EWOULDBLOCK:
#endif
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
      case EAGAIN:
#endif
        res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
        break;
      default:
        res = AMQP_STATUS_SOCKET_ERROR;
    }
  } else {
    self->internal_error = 0;
  }

  return res;
}
static int
amqp_ssl_socket_open(void *base, const char *host, int port, struct timeval *timeout)
{
  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
  long result;
  int status;
  ERR_clear_error();

  self->ssl = SSL_new(self->ctx);
  if (!self->ssl) {
    self->internal_error = ERR_peek_error();
    status = AMQP_STATUS_SSL_ERROR;
    goto exit;
  }

  SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY);
  self->sockfd = amqp_open_socket_noblock(host, port, timeout);
  if (0 > self->sockfd) {
    status = self->sockfd;
    self->internal_error = amqp_os_socket_error();
    self->sockfd = -1;
    goto error_out1;
  }

  status = SSL_set_fd(self->ssl, self->sockfd);
  if (!status) {
    self->internal_error = SSL_get_error(self->ssl, status);
    status = AMQP_STATUS_SSL_ERROR;
    goto error_out2;
  }

  status = SSL_connect(self->ssl);
  if (!status) {
    self->internal_error = SSL_get_error(self->ssl, status);
    status = AMQP_STATUS_SSL_CONNECTION_FAILED;
    goto error_out2;
  }

  result = SSL_get_verify_result(self->ssl);
  if (X509_V_OK != result) {
    self->internal_error = result;
    status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
    goto error_out3;
  }
  if (self->verify) {
    int status = amqp_ssl_socket_verify_hostname(self, host);
    if (status) {
      self->internal_error = 0;
      status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
      goto error_out3;
    }
  }

  self->internal_error = 0;
  status = AMQP_STATUS_OK;

exit:
  return status;

error_out3:
  SSL_shutdown(self->ssl);
error_out2:
  amqp_os_socket_close(self->sockfd);
  self->sockfd = -1;
error_out1:
  SSL_free(self->ssl);
  self->ssl = NULL;
  goto exit;
}
static int
amqp_ssl_socket_open(void *base, const char *host, int port, struct timeval *timeout)
{
  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
  long result;
  int status;
  amqp_time_t deadline;
  X509 *cert;
  BIO *bio;
  if (-1 != self->sockfd) {
    return AMQP_STATUS_SOCKET_INUSE;
  }
  ERR_clear_error();

  self->ssl = SSL_new(self->ctx);
  if (!self->ssl) {
    self->internal_error = ERR_peek_error();
    status = AMQP_STATUS_SSL_ERROR;
    goto exit;
  }

  status = amqp_time_from_now(&deadline, timeout);
  if (AMQP_STATUS_OK != status) {
    return status;
  }

  self->sockfd = amqp_open_socket_inner(host, port, deadline);
  if (0 > self->sockfd) {
    status = self->sockfd;
    self->internal_error = amqp_os_socket_error();
    self->sockfd = -1;
    goto error_out1;
  }

  bio = BIO_new(amqp_openssl_bio());
  if (!bio) {
    status = AMQP_STATUS_NO_MEMORY;
    goto error_out2;
  }

  BIO_set_fd(bio, self->sockfd, BIO_NOCLOSE);
  SSL_set_bio(self->ssl, bio, bio);

start_connect:
  status = SSL_connect(self->ssl);
  if (status != 1) {
    self->internal_error = SSL_get_error(self->ssl, status);
    switch (self->internal_error) {
      case SSL_ERROR_WANT_READ:
        status = amqp_poll(self->sockfd, AMQP_SF_POLLIN, deadline);
        break;
      case SSL_ERROR_WANT_WRITE:
        status = amqp_poll(self->sockfd, AMQP_SF_POLLOUT, deadline);
        break;
      default:
        status = AMQP_STATUS_SSL_CONNECTION_FAILED;
    }
    if (AMQP_STATUS_OK == status) {
      goto start_connect;
    }
    goto error_out2;
  }

  cert = SSL_get_peer_certificate(self->ssl);

  if (self->verify_peer) {
    if (!cert) {
      self->internal_error = 0;
      status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
      goto error_out3;
    }

    result = SSL_get_verify_result(self->ssl);
    if (X509_V_OK != result) {
      self->internal_error = result;
      status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
      goto error_out4;
    }
  }
  if (self->verify_hostname) {
    if (!cert) {
      self->internal_error = 0;
      status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
      goto error_out3;
    }

    if (AMQP_HVR_MATCH_FOUND != amqp_ssl_validate_hostname(host, cert)) {
      self->internal_error = 0;
      status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
      goto error_out4;
    }
  }

  X509_free(cert);
  self->internal_error = 0;
  status = AMQP_STATUS_OK;

exit:
  return status;

error_out4:
  X509_free(cert);
error_out3:
  SSL_shutdown(self->ssl);
error_out2:
  amqp_os_socket_close(self->sockfd);
  self->sockfd = -1;
error_out1:
  SSL_free(self->ssl);
  self->ssl = NULL;
  goto exit;
}