summaryrefslogtreecommitdiff
path: root/network_io
diff options
context:
space:
mode:
authorYann Ylavic <ylavic@apache.org>2022-10-19 15:28:03 +0000
committerYann Ylavic <ylavic@apache.org>2022-10-19 15:28:03 +0000
commitcd279db3cfb82b185c82d1655ac1e629594d6e65 (patch)
tree4bc7de2133a40c1515db4b804c1cac97a5e0d538 /network_io
parent1a53fed88dc63e7f964bc7545d93e742f8938e0f (diff)
downloadapr-cd279db3cfb82b185c82d1655ac1e629594d6e65.tar.gz
apr_socket_sendv: WIN32: Follow up to r1902312: Avoid short writes.
We possibly (edge cases) can't send an entire iovec array in a single WSASend() call because structs WSABUF and iovec are ABI incompatible. To avoid breaking users that rely on full-write by apr_socket_sendv(), which supposedly is guaranteed by WSASend(), repeat the call until the given iovec array is exhausted. There is no way to provide both full-write and atomicity guarantees for apr_socket_sendv() on Windows, so we choose the former.. * include/apr_network_io.h: Document apr_socket_sendv() full-write/atomicity (non-)guarantees above the system ones. * network_io/win32/sendrecv.c(apr_socket_sendv): Change to a loop on WSASend() when needed, taking care of its API limits w.r.t. the given struct iovec. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1904699 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'network_io')
-rw-r--r--network_io/win32/sendrecv.c87
1 files changed, 57 insertions, 30 deletions
diff --git a/network_io/win32/sendrecv.c b/network_io/win32/sendrecv.c
index eb4dd241a..c04d27e29 100644
--- a/network_io/win32/sendrecv.c
+++ b/network_io/win32/sendrecv.c
@@ -20,6 +20,7 @@
#include "apr_network_io.h"
#include "apr_lib.h"
#include "apr_arch_file_io.h"
+#include <assert.h>
#if APR_HAVE_TIME_H
#include <time.h>
#endif
@@ -37,7 +38,11 @@
/* Maximum number of WSABUF allocated for a single apr_socket_sendv() */
#define WSABUF_ON_STACK 50
-#define WSABUF_ON_HEAP 500
+#if APR_SIZEOF_VOIDP < 8
+#define WSABUF_ON_HEAP (APR_DWORD_MAX / sizeof(WSABUF))
+#else
+#define WSABUF_ON_HEAP (APR_DWORD_MAX)
+#endif
APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf,
apr_size_t *len)
@@ -94,59 +99,81 @@ APR_DECLARE(apr_status_t) apr_socket_sendv(apr_socket_t *sock,
apr_status_t rc = APR_SUCCESS;
apr_ssize_t rv;
apr_size_t cur_len;
+ apr_size_t cur_pos = 0;
+ apr_size_t total_len = 0;
apr_size_t nvec = 0;
- apr_size_t n;
int i;
- DWORD dwBytes = 0;
WSABUF *pWsaBuf;
+ *nbytes = 0; /* until further notice */
+
for (i = 0; i < in_vec; i++) {
cur_len = vec[i].iov_len;
-
- while (cur_len > APR_DWORD_MAX) {
- if (nvec >= WSABUF_ON_HEAP) {
- break;
- }
- nvec++;
- cur_len -= APR_DWORD_MAX;
- }
- if (nvec >= WSABUF_ON_HEAP) {
- break;
+ if (!cur_len) {
+ continue;
}
+ if (cur_len > APR_SIZE_MAX - total_len) {
+ /* max total_len can take */
+ return APR_EINVAL;
+ }
+ total_len += cur_len;
nvec++;
}
+ if (!nvec) {
+ return (in_vec >= 0) ? APR_SUCCESS : APR_EINVAL;
+ }
+ if (nvec > WSABUF_ON_HEAP) {
+ /* max malloc() and/or WSASend() can take */
+ nvec = WSABUF_ON_HEAP;
+ }
pWsaBuf = (nvec <= WSABUF_ON_STACK) ? _alloca(sizeof(WSABUF) * (nvec))
: malloc(sizeof(WSABUF) * (nvec));
if (!pWsaBuf)
return APR_ENOMEM;
- for (n = i = 0; n < nvec; i++) {
- char * base = vec[i].iov_base;
- cur_len = vec[i].iov_len;
+ for (i = 0; total_len > 0;) {
+ DWORD nbuf = 0, nsend = 0, nsent = 0;
do {
- if (cur_len > APR_DWORD_MAX) {
- pWsaBuf[n].buf = base;
- pWsaBuf[n].len = APR_DWORD_MAX;
- cur_len -= APR_DWORD_MAX;
- base += APR_DWORD_MAX;
+ assert(i < in_vec);
+ cur_len = vec[i].iov_len - cur_pos;
+ if (!cur_len) {
+ assert(cur_pos == 0);
+ i++;
+ continue;
+ }
+ pWsaBuf[nbuf].buf = (char *)vec[i].iov_base + cur_pos;
+ if (cur_len > APR_DWORD_MAX - nsend) {
+ cur_len = APR_DWORD_MAX - nsend;
+ cur_pos += cur_len;
}
else {
- pWsaBuf[n].buf = base;
- pWsaBuf[n].len = (DWORD)cur_len;
- cur_len = 0;
+ cur_pos = 0;
+ i++;
}
- } while (++n < nvec && cur_len > 0);
- }
- rv = WSASend(sock->socketdes, pWsaBuf, nvec, &dwBytes, 0, NULL, NULL);
- if (rv == SOCKET_ERROR) {
- rc = apr_get_netos_error();
+ nsend += cur_len;
+ pWsaBuf[nbuf++].len = (DWORD)cur_len;
+ } while (nbuf < nvec && nsend < total_len && nsend < APR_DWORD_MAX);
+
+ rv = WSASend(sock->socketdes, pWsaBuf, nbuf, &nsent, 0, NULL, NULL);
+ if (rv == SOCKET_ERROR) {
+ rc = apr_get_netos_error();
+ break;
+ }
+ *nbytes += nsent;
+ if (nsent < nsend) {
+ /* Stop on short/partial write (nonblocking supposedly, but anyway
+ * we don't guarantee full write if the system does not either).
+ */
+ break;
+ }
+ total_len -= nsent;
}
+
if (nvec > WSABUF_ON_STACK)
free(pWsaBuf);
- *nbytes = dwBytes;
return rc;
}