static ssize_t _mongoc_stream_tls_secure_transport_write (mongoc_stream_t *stream, char *buf, size_t buf_len) { OSStatus status; mongoc_stream_tls_t *tls = (mongoc_stream_tls_t *) stream; mongoc_stream_tls_secure_transport_t *secure_transport = (mongoc_stream_tls_secure_transport_t *) tls->ctx; ssize_t write_ret; int64_t now; int64_t expire = 0; ENTRY; BSON_ASSERT (secure_transport); if (tls->timeout_msec >= 0) { expire = bson_get_monotonic_time () + (tls->timeout_msec * 1000UL); } status = SSLWrite ( secure_transport->ssl_ctx_ref, buf, buf_len, (size_t *) &write_ret); switch (status) { case errSSLWouldBlock: case noErr: break; case errSSLClosedAbort: errno = ECONNRESET; default: RETURN (-1); } if (expire) { now = bson_get_monotonic_time (); if ((expire - now) < 0) { if (write_ret < buf_len) { mongoc_counter_streams_timeout_inc (); } tls->timeout_msec = 0; } else { tls->timeout_msec = (expire - now) / 1000L; } } RETURN (write_ret); }
static ssize_t _mongoc_stream_tls_write (mongoc_stream_tls_t *tls, char *buf, size_t buf_len) { ssize_t ret; int64_t now; int64_t expire = 0; BSON_ASSERT (tls); BSON_ASSERT (buf); BSON_ASSERT (buf_len); if (tls->timeout_msec >= 0) { expire = bson_get_monotonic_time () + (tls->timeout_msec * 1000UL); } ret = BIO_write (tls->bio, buf, buf_len); if (ret < 0) { return ret; } if (expire) { now = bson_get_monotonic_time (); if ((expire - now) < 0) { if (ret < buf_len) { mongoc_counter_streams_timeout_inc(); #ifdef _WIN32 errno = WSAETIMEDOUT; #else errno = ETIMEDOUT; #endif } tls->timeout_msec = 0; } else { tls->timeout_msec = (expire - now) / 1000L; } } return ret; }
static ssize_t _mongoc_stream_tls_readv (mongoc_stream_t *stream, mongoc_iovec_t *iov, size_t iovcnt, size_t min_bytes, int32_t timeout_msec) { mongoc_stream_tls_t *tls = (mongoc_stream_tls_t *)stream; ssize_t ret = 0; size_t i; int read_ret; size_t iov_pos = 0; int64_t now; int64_t expire = 0; BSON_ASSERT (tls); BSON_ASSERT (iov); BSON_ASSERT (iovcnt); tls->timeout_msec = timeout_msec; if (timeout_msec >= 0) { expire = bson_get_monotonic_time () + (timeout_msec * 1000UL); } for (i = 0; i < iovcnt; i++) { iov_pos = 0; while (iov_pos < iov[i].iov_len) { read_ret = BIO_read (tls->bio, (char *)iov[i].iov_base + iov_pos, (int)(iov[i].iov_len - iov_pos)); if (read_ret < 0) { return read_ret; } if (expire) { now = bson_get_monotonic_time (); if ((expire - now) < 0) { if (read_ret == 0) { mongoc_counter_streams_timeout_inc(); #ifdef _WIN32 errno = WSAETIMEDOUT; #else errno = ETIMEDOUT; #endif return -1; } tls->timeout_msec = 0; } else { tls->timeout_msec = (expire - now) / 1000L; } } ret += read_ret; if ((size_t)ret >= min_bytes) { mongoc_counter_streams_ingress_add(ret); return ret; } iov_pos += read_ret; } } if (ret >= 0) { mongoc_counter_streams_ingress_add(ret); } return ret; }
static bool _mongoc_socket_wait (mongoc_socket_t *sock, /* IN */ int events, /* IN */ int64_t expire_at) /* IN */ { #ifdef _WIN32 fd_set read_fds; fd_set write_fds; fd_set error_fds; struct timeval timeout_tv; #else struct pollfd pfd; #endif int ret; int timeout; int64_t now; ENTRY; BSON_ASSERT (sock); BSON_ASSERT (events); #ifdef _WIN32 FD_ZERO (&read_fds); FD_ZERO (&write_fds); FD_ZERO (&error_fds); if (events & POLLIN) { FD_SET (sock->sd, &read_fds); } if (events & POLLOUT) { FD_SET (sock->sd, &write_fds); } FD_SET (sock->sd, &error_fds); #else pfd.fd = sock->sd; pfd.events = events | POLLERR | POLLHUP; pfd.revents = 0; #endif now = bson_get_monotonic_time (); for (;;) { if (expire_at < 0) { timeout = -1; } else if (expire_at == 0) { timeout = 0; } else { timeout = (int) ((expire_at - now) / 1000L); if (timeout < 0) { timeout = 0; } } #ifdef _WIN32 if (timeout == -1) { /* not WSAPoll: daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken */ ret = select (0 /*unused*/, &read_fds, &write_fds, &error_fds, NULL); } else { timeout_tv.tv_sec = timeout / 1000; timeout_tv.tv_usec = (timeout % 1000) * 1000; ret = select ( 0 /*unused*/, &read_fds, &write_fds, &error_fds, &timeout_tv); } if (ret == SOCKET_ERROR) { _mongoc_socket_capture_errno (sock); ret = -1; } else if (FD_ISSET (sock->sd, &error_fds)) { errno = WSAECONNRESET; ret = -1; } #else ret = poll (&pfd, 1, timeout); #endif if (ret > 0) { /* Something happened, so return that */ #ifdef _WIN32 return (FD_ISSET (sock->sd, &read_fds) || FD_ISSET (sock->sd, &write_fds)); #else RETURN (0 != (pfd.revents & events)); #endif } else if (ret < 0) { /* poll itself failed */ TRACE ("errno is: %d", errno); if (MONGOC_ERRNO_IS_AGAIN (errno)) { now = bson_get_monotonic_time (); if (expire_at < now) { _mongoc_socket_capture_errno (sock); RETURN (false); } else { continue; } } else { /* poll failed for some non-transient reason */ _mongoc_socket_capture_errno (sock); RETURN (false); } } else { /* ret == 0, poll timed out */ if (timeout) { mongoc_counter_streams_timeout_inc (); } #ifdef _WIN32 sock->errno_ = timeout ? WSAETIMEDOUT : EAGAIN; #else sock->errno_ = timeout ? ETIMEDOUT : EAGAIN; #endif RETURN (false); } } }
static ssize_t _mongoc_socket_try_sendv_slow (mongoc_socket_t *sock, /* IN */ mongoc_iovec_t *iov, /* IN */ size_t iovcnt) /* IN */ { ssize_t ret = 0; size_t i; ssize_t wrote; ENTRY; BSON_ASSERT (sock); BSON_ASSERT (iov); BSON_ASSERT (iovcnt); for (i = 0; i < iovcnt; i++) { wrote = send (sock->sd, iov[i].iov_base, iov[i].iov_len, 0); #ifdef _WIN32 if (wrote == SOCKET_ERROR) { #else if (wrote == -1) { #endif _mongoc_socket_capture_errno (sock); if (!_mongoc_socket_errno_is_again (sock)) { RETURN (-1); } RETURN (ret ? ret : -1); } ret += wrote; if (wrote != iov[i].iov_len) { RETURN (ret); } } RETURN (ret); } /* *-------------------------------------------------------------------------- * * _mongoc_socket_try_sendv -- * * Helper used by mongoc_socket_sendv() to try to write as many * bytes to the underlying socket until the socket buffer is full. * * This is performed in a non-blocking fashion. * * Returns: * -1 on failure. the number of bytes written on success. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static ssize_t _mongoc_socket_try_sendv (mongoc_socket_t *sock, /* IN */ mongoc_iovec_t *iov, /* IN */ size_t iovcnt) /* IN */ { #ifdef _WIN32 DWORD dwNumberofBytesSent = 0; int ret; #else struct msghdr msg; ssize_t ret; #endif ENTRY; BSON_ASSERT (sock); BSON_ASSERT (iov); BSON_ASSERT (iovcnt); DUMP_IOVEC (sendbuf, iov, iovcnt); #ifdef _WIN32 ret = WSASend ( sock->sd, (LPWSABUF) iov, iovcnt, &dwNumberofBytesSent, 0, NULL, NULL); TRACE ("WSASend sent: %ld (out of: %ld), ret: %d", dwNumberofBytesSent, iov->iov_len, ret); #else memset (&msg, 0, sizeof msg); msg.msg_iov = iov; msg.msg_iovlen = (int) iovcnt; ret = sendmsg (sock->sd, &msg, #ifdef MSG_NOSIGNAL MSG_NOSIGNAL); #else 0); #endif TRACE ("Send %ld out of %ld bytes", ret, iov->iov_len); #endif #ifdef _WIN32 if (ret == SOCKET_ERROR) { #else if (ret == -1) { #endif _mongoc_socket_capture_errno (sock); /* * Check to see if we have sent an iovec too large for sendmsg to * complete. If so, we need to fallback to the slow path of multiple * send() commands. */ #ifdef _WIN32 if (mongoc_socket_errno (sock) == WSAEMSGSIZE) { #else if (mongoc_socket_errno (sock) == EMSGSIZE) { #endif RETURN (_mongoc_socket_try_sendv_slow (sock, iov, iovcnt)); } RETURN (-1); } #ifdef _WIN32 RETURN (dwNumberofBytesSent); #else RETURN (ret); #endif } /* *-------------------------------------------------------------------------- * * mongoc_socket_sendv -- * * A wrapper around using sendmsg() to send an iovec. * This also deals with the structure differences between * WSABUF and struct iovec. * * @expire_at is 0 for no blocking, -1 for infinite blocking, * or a time using the monotonic clock to expire. Calculate this * using bson_get_monotonic_time() + N_MICROSECONDS. * * Returns: * -1 on failure. * the number of bytes written on success. * * Side effects: * None. * *-------------------------------------------------------------------------- */ ssize_t mongoc_socket_sendv (mongoc_socket_t *sock, /* IN */ mongoc_iovec_t *in_iov, /* IN */ size_t iovcnt, /* IN */ int64_t expire_at) /* IN */ { ssize_t ret = 0; ssize_t sent; size_t cur = 0; mongoc_iovec_t *iov; ENTRY; BSON_ASSERT (sock); BSON_ASSERT (in_iov); BSON_ASSERT (iovcnt); iov = bson_malloc (sizeof (*iov) * iovcnt); memcpy (iov, in_iov, sizeof (*iov) * iovcnt); for (;;) { sent = _mongoc_socket_try_sendv (sock, &iov[cur], iovcnt - cur); TRACE ( "Sent %ld (of %ld) out of iovcnt=%ld", sent, iov[cur].iov_len, iovcnt); /* * If we failed with anything other than EAGAIN or EWOULDBLOCK, * we should fail immediately as there is another issue with the * underlying socket. */ if (sent == -1) { if (!_mongoc_socket_errno_is_again (sock)) { ret = -1; GOTO (CLEANUP); } } /* * Update internal stream counters. */ if (sent > 0) { ret += sent; mongoc_counter_streams_egress_add (sent); /* * Subtract the sent amount from what we still need to send. */ while ((cur < iovcnt) && (sent >= (ssize_t) iov[cur].iov_len)) { TRACE ("still got bytes left: sent -= iov_len: %ld -= %ld", sent, iov[cur].iov_len); sent -= iov[cur++].iov_len; } /* * Check if that made us finish all of the iovecs. If so, we are done * sending data over the socket. */ if (cur == iovcnt) { TRACE ("%s", "Finished the iovecs"); break; } /* * Increment the current iovec buffer to its proper offset and adjust * the number of bytes to write. */ TRACE ("Seeked io_base+%ld", sent); TRACE ( "Subtracting iov_len -= sent; %ld -= %ld", iov[cur].iov_len, sent); iov[cur].iov_base = ((char *) iov[cur].iov_base) + sent; iov[cur].iov_len -= sent; TRACE ("iov_len remaining %ld", iov[cur].iov_len); BSON_ASSERT (iovcnt - cur); BSON_ASSERT (iov[cur].iov_len); } else if (OPERATION_EXPIRED (expire_at)) { if (expire_at > 0) { mongoc_counter_streams_timeout_inc (); } GOTO (CLEANUP); } /* * Block on poll() until our desired condition is met. */ if (!_mongoc_socket_wait (sock, POLLOUT, expire_at)) { GOTO (CLEANUP); } } CLEANUP: bson_free (iov); RETURN (ret); } int mongoc_socket_getsockname (mongoc_socket_t *sock, /* IN */ struct sockaddr *addr, /* OUT */ mongoc_socklen_t *addrlen) /* INOUT */ { int ret; ENTRY; BSON_ASSERT (sock); ret = getsockname (sock->sd, addr, addrlen); _mongoc_socket_capture_errno (sock); RETURN (ret); }
/* This function is copypasta of _mongoc_stream_tls_openssl_readv */ static ssize_t _mongoc_stream_tls_secure_transport_readv (mongoc_stream_t *stream, mongoc_iovec_t *iov, size_t iovcnt, size_t min_bytes, int32_t timeout_msec) { mongoc_stream_tls_t *tls = (mongoc_stream_tls_t *) stream; mongoc_stream_tls_secure_transport_t *secure_transport = (mongoc_stream_tls_secure_transport_t *) tls->ctx; ssize_t ret = 0; size_t i; size_t read_ret; size_t iov_pos = 0; int64_t now; int64_t expire = 0; BSON_ASSERT (iov); BSON_ASSERT (iovcnt); BSON_ASSERT (secure_transport); ENTRY; tls->timeout_msec = timeout_msec; if (timeout_msec >= 0) { expire = bson_get_monotonic_time () + (timeout_msec * 1000UL); } for (i = 0; i < iovcnt; i++) { iov_pos = 0; while (iov_pos < iov[i].iov_len) { OSStatus status = SSLRead (secure_transport->ssl_ctx_ref, (char *) iov[i].iov_base + iov_pos, (int) (iov[i].iov_len - iov_pos), &read_ret); if (status != noErr) { RETURN (-1); } if (expire) { now = bson_get_monotonic_time (); if ((expire - now) < 0) { if (read_ret == 0) { mongoc_counter_streams_timeout_inc (); errno = ETIMEDOUT; RETURN (-1); } tls->timeout_msec = 0; } else { tls->timeout_msec = (expire - now) / 1000L; } } ret += read_ret; if ((size_t) ret >= min_bytes) { mongoc_counter_streams_ingress_add (ret); RETURN (ret); } iov_pos += read_ret; } } if (ret >= 0) { mongoc_counter_streams_ingress_add (ret); } RETURN (ret); }