]> git.meshlink.io Git - catta/commitdiff
add (untested) implementation of recvmsg in terms of WSARecvMsg
authorSven M. Hallberg <pesco@khjk.org>
Thu, 28 Aug 2014 18:36:55 +0000 (20:36 +0200)
committerSven M. Hallberg <pesco@khjk.org>
Thu, 28 Aug 2014 18:36:55 +0000 (20:36 +0200)
src/compat/windows/include/sys/uio.h [new file with mode: 0644]
src/compat/windows/wincompat.c
src/compat/windows/wincompat.h

diff --git a/src/compat/windows/include/sys/uio.h b/src/compat/windows/include/sys/uio.h
new file mode 100644 (file)
index 0000000..cc77ee8
--- /dev/null
@@ -0,0 +1 @@
+#include "../../wincompat.h"
index 3aa2b2e5d9b00ec7615cc830b32cc2eb6683b65a..d0d239cc7924f10542e8c81a37c785b85c5db86c 100644 (file)
@@ -1,5 +1,113 @@
 #include "wincompat.h"
 #include <errno.h>
 #include "wincompat.h"
 #include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdint.h>
+
+// helper: convert WSAGetLastError() to an errno constant
+static int wsa_errno(void)
+{
+    switch(WSAGetLastError()) {
+        case WSAECONNRESET:     return ECONNRESET;
+        case WSAEFAULT:         return EFAULT;
+        case WSAEINPROGRESS:    return EINPROGRESS;
+        case WSAEINTR:          return EINTR;
+        case WSAEINVAL:         return EINVAL;
+        case WSAEMSGSIZE:       return EMSGSIZE;
+        case WSAENETDOWN:       return ENETDOWN;
+        case WSAENETRESET:      return ENETRESET;
+        case WSAENOTCONN:       return ENOTCONN;
+        case WSAENOTSOCK:       return ENOTSOCK;
+        case WSAEOPNOTSUPP:     return EOPNOTSUPP;
+        case WSAEWOULDBLOCK:    return EWOULDBLOCK;
+        default:
+            return EINVAL;
+    }
+}
+
+ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
+{
+    LPFN_WSARECVMSG WSARecvMsg = NULL;
+    GUID wsaid = WSAID_WSARECVMSG;
+    DWORD b;
+
+    DWORD bytesrcvd;
+    WSAMSG wsamsg;
+    size_t i;
+    int r;
+
+    // size_t is larger than DWORD on 64bit
+    if(msg->msg_iovlen > UINT32_MAX) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    // obtain the function pointer to WSARecvMsg
+    r = WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                 &wsaid, sizeof(wsaid), &WSARecvMsg, sizeof(WSARecvMsg),
+                 &b, NULL, NULL);
+    if(r == SOCKET_ERROR) {
+        errno = wsa_errno();
+        return -1;
+    }
+    assert(b == sizeof(WSARecvMsg));
+    assert(WSARecvMsg != NULL);
+
+    // convert msghdr to WSAMSG structure
+    wsamsg.name = msg->msg_name;
+    wsamsg.namelen = msg->msg_namelen;
+    wsamsg.lpBuffers = malloc(msg->msg_iovlen * sizeof(WSABUF));
+    wsamsg.dwBufferCount = msg->msg_iovlen;
+    wsamsg.Control.len = msg->msg_controllen;
+    wsamsg.Control.buf = msg->msg_control;
+    wsamsg.dwFlags = (DWORD)flags;
+
+    // all flags that fit into dwFlags also fit through the flags argument
+    assert(sizeof(DWORD) <= sizeof(flags));
+
+    if(wsamsg.lpBuffers == NULL) {
+        // malloc will have set errno
+        return -1;
+    }
+
+    // re-wrap iovecs as WSABUFs
+    for(i=0; i<msg->msg_iovlen; i++) {
+        // size_t vs. u_long
+        if(msg->msg_iov[i].iov_len > ULONG_MAX) {
+            free(wsamsg.lpBuffers);
+            errno = EINVAL;
+            return -1;
+        }
+
+        wsamsg.lpBuffers[i].len = msg->msg_iov[i].iov_len;
+        wsamsg.lpBuffers[i].buf = msg->msg_iov[i].iov_base;
+    }
+
+    r = WSARecvMsg(sockfd, &wsamsg, &bytesrcvd, NULL, NULL);
+
+    // the allocated WSABUF wrappers are no longer needed
+    free(wsamsg.lpBuffers);
+
+    if(r == SOCKET_ERROR) {
+        // XXX do we need special handling for ENETRESET, EMSGSIZE, ETIMEDOUT?
+        errno = wsa_errno();
+        return -1;
+    }
+
+    // DWORD has one bit more than ssize_t on 32bit
+    if(bytesrcvd > SSIZE_MAX) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    // transfer results from wsamsg to msg
+    // NB: the data and control buffers are shared
+    msg->msg_controllen = wsamsg.Control.len;
+    msg->msg_flags = (int)wsamsg.dwFlags;
+        // all flags that fit into dwFlags also fit into msg_flags (see above)
+
+    return bytesrcvd;
+}
 
 int uname(struct utsname *buf)
 {
 
 int uname(struct utsname *buf)
 {
index 531aadd8b0c47e1ae9ec503827773c3b63295090..6510183a870b86f95644cd6b7c64ce9bfdff9f19 100644 (file)
@@ -1,15 +1,76 @@
 #ifndef foowincompatfoo
 #define foowincompatfoo
 
 #ifndef foowincompatfoo
 #define foowincompatfoo
 
+// This file and its companion wincompat.c provide some Posix interfaces to
+// Windows APIs so the rest of the code can keep using them.
+
+
+// require at least Windows Vista
 #undef WINVER
 #undef _WIN32_WINNT
 #undef WINVER
 #undef _WIN32_WINNT
-
-#define WINVER 0x0600       // Vista
+#define WINVER 0x0600
 #define _WIN32_WINNT WINVER
 
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #define _WIN32_WINNT WINVER
 
 #include <winsock2.h>
 #include <ws2tcpip.h>
+#include <mswsock.h>
+
+
+// Winsock doesn't have recvmsg/sendmsg but offers the same functionality
+// with WSARecvMsg/WSASendMsg, so we implement the former in terms of the
+// latter.
+
+struct iovec {                   /* Scatter/gather array items */
+   void  *iov_base;              /* Starting address */
+   size_t iov_len;               /* Number of bytes to transfer */
+};
+
+struct msghdr {
+   void         *msg_name;       /* optional address */
+   socklen_t     msg_namelen;    /* size of address */
+   struct iovec *msg_iov;        /* scatter/gather array */
+   size_t        msg_iovlen;     /* # elements in msg_iov */
+   void         *msg_control;    /* ancillary data, see below */
+   size_t        msg_controllen; /* ancillary data buffer len */
+   int           msg_flags;      /* flags on received message */
+};
+
+// MSDN says this struct is called wsacmsghdr but MingW uses _WSACMSGHDR.
+// TODO: Verify what it is on actual Windows.
+// cf. http://msdn.microsoft.com/en-us/library/ms741645(v=vs.85).aspx
+#ifdef __MINGW32__
+#define cmsghdr _WSACMSGHDR     // as in 'struct cmsghdr'
+#else
+#define cmsghdr wsacmsghdr      // as in 'struct cmsghdr'
+#endif
+
+static inline struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *m) {
+    WSAMSG wm;
+    wm.Control.len = m->msg_controllen;
+    wm.Control.buf = m->msg_control;
+    return WSA_CMSG_FIRSTHDR(&wm);
+}
+
+static inline struct cmsghdr *CMSG_NXTHDR(struct msghdr *m, struct cmsghdr *c) {
+    WSAMSG wm;
+    wm.Control.len = m->msg_controllen;
+    wm.Control.buf = m->msg_control;
+    return WSA_CMSG_NXTHDR(&wm, c);
+}
+
+#define CMSG_SPACE(len) WSA_CMSG_SPACE(len)
+#define CMSG_LEN(len) WSA_CMSG_LEN(len)
+
+// we're going to be naughty and redefine CMSG_DATA as an alias even though it
+// is also a constant defined in wincrypt.h which we don't care about.
+#undef CMSG_DATA
+#define CMSG_DATA(c) WSA_CMSG_DATA(c)
+
+ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
+ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
+
 
 
+// Windows logically doesn't have uname, so we supply a replacement.
 
 struct utsname {
    char sysname[9];    /* Operating system name (e.g., "Linux") */
 
 struct utsname {
    char sysname[9];    /* Operating system name (e.g., "Linux") */