Commit bfd453f3 authored by Evgeny Grin's avatar Evgeny Grin
Browse files

Sockets: implement sendmsg()-like functions on Win32



Use WSASend() to send several buffer per one sys-call.
Unified send()/recv() support for POSIX/Win32.
Signed-off-by: Evgeny Grin's avatarEvgeny Grin <k2k@narod.ru>
parent 67ccc7d3
......@@ -588,16 +588,12 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
#endif
handshake_internal_state_clear1(*session);
#ifdef HAVE_WRITEV
#ifdef MSG_NOSIGNAL
if (flags & GNUTLS_NO_SIGNAL)
gnutls_transport_set_vec_push_function(*session, system_writev_nosignal);
else
#endif
gnutls_transport_set_vec_push_function(*session, system_writev);
#else
gnutls_transport_set_push_function(*session, system_write);
#endif
(*session)->internals.pull_timeout_func = gnutls_system_recv_timeout;
(*session)->internals.pull_func = system_read;
(*session)->internals.errno_func = system_errno;
......
......@@ -52,10 +52,8 @@ extern CertEnumCRLsInStoreFunc pCertEnumCRLsInStore;
int system_errno(gnutls_transport_ptr_t);
#ifdef _WIN32
ssize_t system_write(gnutls_transport_ptr_t ptr, const void *data,
size_t data_size);
#else
#define HAVE_WRITEV
ssize_t system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
int iovec_cnt);
......@@ -65,7 +63,6 @@ ssize_t system_writev_tfo(gnutls_session_t ptr, const giovec_t * iovec,
int iovec_cnt);
ssize_t system_writev_nosignal_tfo(gnutls_session_t ptr, const giovec_t * iovec,
int iovec_cnt);
#endif
ssize_t system_read(gnutls_transport_ptr_t ptr, void *data,
size_t data_size);
......
......@@ -55,7 +55,7 @@
#ifdef _WIN32
static ssize_t
tfo_send(gnutls_transport_ptr_t ptr, const void *buf, size_t len)
tfo_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec, int iovec_cnt)
{
tfo_st *p = ptr;
int fd = p->fd;
......@@ -63,7 +63,8 @@ tfo_send(gnutls_transport_ptr_t ptr, const void *buf, size_t len)
if (unlikely(p->connect_addrlen != 0)) {
int ret;
ret = connect(fd, (struct sockaddr*)&p->connect_addr, p->connect_addrlen);
ret = connect(fd, (struct sockaddr*)&p->connect_addr,
p->connect_addrlen);
if (ret == -1 && (errno == EINPROGRESS)) {
gnutls_assert();
errno = EAGAIN;
......@@ -77,7 +78,7 @@ tfo_send(gnutls_transport_ptr_t ptr, const void *buf, size_t len)
return ret;
}
return send(fd, buf, len, 0);
return system_writev(GNUTLS_INT_TO_POINTER(fd), iovec, iovec_cnt);
}
#else /* sendmsg */
static ssize_t
......@@ -239,11 +240,6 @@ gnutls_transport_set_fastopen(gnutls_session_t session,
session->internals.tfo.flags |= MSG_NOSIGNAL;
#endif
#ifdef _WIN32
gnutls_transport_set_vec_push_function(session, NULL);
gnutls_transport_set_push_function(session, tfo_send);
#else
gnutls_transport_set_vec_push_function(session, tfo_writev);
#endif
}
......@@ -79,6 +79,48 @@ system_write(gnutls_transport_ptr ptr, const void *data, size_t data_size)
{
return send(GNUTLS_POINTER_TO_INT(ptr), data, data_size, 0);
}
ssize_t
system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
int iovec_cnt)
{
WSABUF bufs[iovec_cnt];
  • This does not work with MSVC. Error:

    system/sockets.c(87): error C2057: expected constant expression
    system/sockets.c(87): error C2466: cannot allocate an array of constant size 0
    system/sockets.c(87): error C2133: 'bufs': unknown size
  • That's correct, MSVC does not support variable length array. I was not aware that GnuTLS supports compiling by MSVC. I'll make a patch.

  • I replaced with: WSABUF *bufs = alloca (sizeof(WSABUF) * iovec_cnt); to fix it here.

  • Maybe a better solution would be small fixed size + malloced version for large size? This way we can avoid stack overflow in special cases.

    alloc() on Windows has some limitations: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/alloca?view=msvc-170

  • Surely there must be a upper-limit on iovec_cnt?
    E.g. #define IOVEC_MAX_CNT 20 or something.

  • There is no limit on WinNT-based Windows versions. It was limited to 16 buffers for Windows 9x/ME, but it was a very long time ago. 😄

    By the way, POSIX version does not check for the limits, while Solaris (for example) still limited to 16 buffers.

  • The fix: !1489 (closed)

Please register or sign in to reply
DWORD bytes_sent;
int to_send_cnt = 0;
size_t to_send_bytes = 0;
while (to_send_cnt < iovec_cnt && to_send_bytes < SSIZE_MAX) {
bufs[to_send_cnt].buf = iovec[to_send_cnt].iov_base;
if (to_send_bytes + iovec[to_send_cnt].iov_len > SSIZE_MAX) {
/* Return value limit: successful result value cannot
* exceed SSIZE_MAX */
size_t space_left = (size_t)SSIZE_MAX - to_send_bytes;
bufs[to_send_cnt].len = (unsigned long)
(space_left > ULONG_MAX ?
ULONG_MAX : space_left);
to_send_cnt++;
break;
}
if (iovec[to_send_cnt].iov_len > ULONG_MAX) {
/* WSASend() limitation */
bufs[to_send_cnt].len = ULONG_MAX;
to_send_cnt++;
break;
}
bufs[to_send_cnt].len =
(unsigned long) iovec[to_send_cnt].iov_len;
to_send_bytes += iovec[to_send_cnt].iov_len;
to_send_cnt++;
}
if (WSASend(GNUTLS_POINTER_TO_INT(ptr), bufs, to_send_cnt, &bytes_sent,
0, NULL, NULL) != 0)
return -1;
return (ssize_t)bytes_sent;
}
#else /* POSIX */
int system_errno(gnutls_transport_ptr_t ptr)
{
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment