From: Sven M. Hallberg Date: Thu, 28 Aug 2014 18:36:55 +0000 (+0200) Subject: add (untested) implementation of recvmsg in terms of WSARecvMsg X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=f8ac9afd843c50ab15d7645d6479f65e0ec562cc;p=catta add (untested) implementation of recvmsg in terms of WSARecvMsg --- diff --git a/src/compat/windows/include/sys/uio.h b/src/compat/windows/include/sys/uio.h new file mode 100644 index 0000000..cc77ee8 --- /dev/null +++ b/src/compat/windows/include/sys/uio.h @@ -0,0 +1 @@ +#include "../../wincompat.h" diff --git a/src/compat/windows/wincompat.c b/src/compat/windows/wincompat.c index 3aa2b2e..d0d239c 100644 --- a/src/compat/windows/wincompat.c +++ b/src/compat/windows/wincompat.c @@ -1,5 +1,113 @@ #include "wincompat.h" #include +#include +#include +#include + +// 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; imsg_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) { diff --git a/src/compat/windows/wincompat.h b/src/compat/windows/wincompat.h index 531aadd..6510183 100644 --- a/src/compat/windows/wincompat.h +++ b/src/compat/windows/wincompat.h @@ -1,15 +1,76 @@ #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 - -#define WINVER 0x0600 // Vista +#define WINVER 0x0600 #define _WIN32_WINNT WINVER #include #include +#include + + +// 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") */