From a4f63a818eea476efa456132ca6112329ef8a484 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Tue, 27 Jan 2009 06:04:32 +0000 Subject: [PATCH] rx-windows-xmit-20090126 LICENSE MIT The WinSock API does not include the Posix recvmsg/sendmsg interfaces. Beginning with XP/2003 Microsoft began to support WSARecvMsg which is a mostly compatible implementation of recvmsg. In Vista/2008 Microsoft began to support WSASendMsg a mostly compatible implementation of sendmsg. Neither are part of the WinSock API and therefore they must be loaded at runtime via a WSAIoctl() call to obtain the function pointers. When the functions are available it is now possible to avoid a large number of memcpy() calls. This patch also enables UDP port unreachable messages on XP and above. --- src/rx/rx_user.c | 4 + src/rx/rx_xmit_nt.c | 318 +++++++++++++++++++++++++++++++------------- src/rx/rx_xmit_nt.h | 7 +- 3 files changed, 231 insertions(+), 98 deletions(-) diff --git a/src/rx/rx_user.c b/src/rx/rx_user.c index ddb0b728b..1a059d90a 100644 --- a/src/rx/rx_user.c +++ b/src/rx/rx_user.c @@ -130,6 +130,10 @@ rxi_GetHostUDPSocket(u_int ahost, u_short port) goto error; } +#ifdef AFS_NT40_ENV + rxi_xmit_init(socketFd); +#endif /* AFS_NT40_ENV */ + taddr.sin_addr.s_addr = ahost; taddr.sin_family = AF_INET; taddr.sin_port = (u_short) port; diff --git a/src/rx/rx_xmit_nt.c b/src/rx/rx_xmit_nt.c index 12fec947d..255eb8f41 100644 --- a/src/rx/rx_xmit_nt.c +++ b/src/rx/rx_xmit_nt.c @@ -20,63 +20,152 @@ RCSID ("$Header$"); -#if defined(AFS_NT40_ENV) +#if defined(AFS_NT40_ENV) #include +#include + +#if (_WIN32_WINNT < 0x0600) +/* + * WSASendMsg -- send data to a specific destination, with options, using + * overlapped I/O where applicable. + * + * Valid flags for dwFlags parameter: + * MSG_DONTROUTE + * MSG_PARTIAL (a.k.a. MSG_EOR) (only for non-stream sockets) + * MSG_OOB (only for stream style sockets) (NYI) + * + * Caller must provide either lpOverlapped or lpCompletionRoutine + * or neither (both NULL). + */ +typedef +INT +(PASCAL FAR * LPFN_WSASENDMSG) ( + IN SOCKET s, + IN LPWSAMSG lpMsg, + IN DWORD dwFlags, + __out_opt LPDWORD lpNumberOfBytesSent, + IN LPWSAOVERLAPPED lpOverlapped OPTIONAL, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine OPTIONAL + ); + +#define WSAID_WSASENDMSG /* a441e712-754f-43ca-84a7-0dee44cf606d */ \ + {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}} +#endif /* _WINNT_WIN32 */ -#include "rx_internal.h" #include "rx.h" #include "rx_packet.h" #include "rx_globals.h" #include "rx_xmit_nt.h" -#ifdef AFS_NT40_ENV #include -#endif #include + +/* + * WSASendMsg is only supported on Vista and above + * Neither function is part of the public WinSock API + * and therefore the function pointers must be + * obtained via WSAIoctl() + */ +static LPFN_WSARECVMSG pWSARecvMsg = NULL; +static LPFN_WSASENDMSG pWSASendMsg = NULL; + +void +rxi_xmit_init(osi_socket s) +{ + int rc; + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + GUID WSASendMsg_GUID = WSAID_WSASENDMSG; + DWORD dwIn, dwOut, NumberOfBytes; + + rc = WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID), + &pWSARecvMsg, sizeof(pWSARecvMsg), + &NumberOfBytes, NULL, NULL); + + rc = WSAIoctl( s, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSASendMsg_GUID, sizeof(WSASendMsg_GUID), + &pWSASendMsg, sizeof(pWSASendMsg), + &NumberOfBytes, NULL, NULL); + + /* Turn on UDP PORT_UNREACHABLE messages */ + dwIn = 1; + rc = WSAIoctl( s, SIO_UDP_CONNRESET, + &dwIn, sizeof(dwIn), + &dwOut, sizeof(dwOut), + &NumberOfBytes, NULL, NULL); +} + int recvmsg(osi_socket socket, struct msghdr *msgP, int flags) { - char rbuf[RX_MAX_PACKET_SIZE]; - int size; int code; - int off, i, n; - int allocd = 0; - - - size = rx_maxJumboRecvSize; - code = - recvfrom((SOCKET) socket, rbuf, size, flags, - (struct sockaddr *)(msgP->msg_name), &(msgP->msg_namelen)); - - if (code > 0) { - size = code; - - for (off = i = 0; size > 0 && i < msgP->msg_iovlen; i++) { - if (msgP->msg_iov[i].iov_len) { - if (msgP->msg_iov[i].iov_len < size) { - n = msgP->msg_iov[i].iov_len; - } else { - n = size; - } - memcpy(msgP->msg_iov[i].iov_base, &rbuf[off], n); - off += n; - size -= n; - } - } - - /* Accounts for any we didn't copy in to iovecs. */ - code -= size; + + if (pWSARecvMsg) { + WSAMSG wsaMsg; + DWORD dwBytes; + + wsaMsg.name = (LPSOCKADDR)(msgP->msg_name); + wsaMsg.namelen = (INT)(msgP->msg_namelen); + + wsaMsg.lpBuffers = (LPWSABUF) msgP->msg_iov; + wsaMsg.dwBufferCount = msgP->msg_iovlen; + wsaMsg.Control.len = 0; + wsaMsg.Control.buf = NULL; + wsaMsg.dwFlags = flags; + + code = pWSARecvMsg(socket, &wsaMsg, &dwBytes, NULL, NULL); + if (code == 0) { + /* success - return the number of bytes read */ + code = (int)dwBytes; + } else { + /* error - set errno and return -1 */ + if (code == SOCKET_ERROR) + code = WSAGetLastError(); + if (code == WSAEWOULDBLOCK || code == WSAECONNRESET) + errno = WSAEWOULDBLOCK; + else + errno = EIO; + code = -1; + } } else { -#ifdef AFS_NT40_ENV - if (code == SOCKET_ERROR) - code = WSAGetLastError(); - if (code == WSAEWOULDBLOCK || code == WSAECONNRESET) - errno = WSAEWOULDBLOCK; - else - errno = EIO; -#endif /* AFS_NT40_ENV */ - code = -1; + char rbuf[RX_MAX_PACKET_SIZE]; + int size; + int off, i, n; + int allocd = 0; + + size = rx_maxJumboRecvSize; + code = + recvfrom((SOCKET) socket, rbuf, size, flags, + (struct sockaddr *)(msgP->msg_name), &(msgP->msg_namelen)); + + if (code > 0) { + size = code; + + for (off = i = 0; size > 0 && i < msgP->msg_iovlen; i++) { + if (msgP->msg_iov[i].iov_len) { + if (msgP->msg_iov[i].iov_len < size) { + n = msgP->msg_iov[i].iov_len; + } else { + n = size; + } + memcpy(msgP->msg_iov[i].iov_base, &rbuf[off], n); + off += n; + size -= n; + } + } + + /* Accounts for any we didn't copy in to iovecs. */ + code -= size; + } else { + if (code == SOCKET_ERROR) + code = WSAGetLastError(); + if (code == WSAEWOULDBLOCK || code == WSAECONNRESET) + errno = WSAEWOULDBLOCK; + else + errno = EIO; + code = -1; + } } return code; @@ -85,67 +174,106 @@ recvmsg(osi_socket socket, struct msghdr *msgP, int flags) int sendmsg(osi_socket socket, struct msghdr *msgP, int flags) { - char buf[RX_MAX_PACKET_SIZE]; - char *sbuf = buf; - int size, tmp; int code; - int off, i, n; - int allocd = 0; - for (size = i = 0; i < msgP->msg_iovlen; i++) - size += msgP->msg_iov[i].iov_len; + if (pWSASendMsg) { + WSAMSG wsaMsg; + DWORD dwBytes; - if (msgP->msg_iovlen <= 2) { - sbuf = msgP->msg_iov[0].iov_base; + wsaMsg.name = (LPSOCKADDR)(msgP->msg_name); + wsaMsg.namelen = (INT)(msgP->msg_namelen); + + wsaMsg.lpBuffers = (LPWSABUF) msgP->msg_iov; + wsaMsg.dwBufferCount = msgP->msg_iovlen; + wsaMsg.Control.len = 0; + wsaMsg.Control.buf = NULL; + wsaMsg.dwFlags = 0; + + code = pWSASendMsg(socket, &wsaMsg, flags, &dwBytes, NULL, NULL); + if (code == 0) { + /* success - return the number of bytes read */ + code = (int)dwBytes; + } else { + /* error - set errno and return -1 */ + if (code == SOCKET_ERROR) + code = WSAGetLastError(); + switch (code) { + case WSAEINPROGRESS: + case WSAENETRESET: + case WSAENOBUFS: + errno = 0; + break; + case WSAEWOULDBLOCK: + case WSAECONNRESET: + errno = WSAEWOULDBLOCK; + break; + case WSAEHOSTUNREACH: + errno = WSAEHOSTUNREACH; + break; + default: + errno = EIO; + break; + } + code = -1; + } } else { - /* Pack data into array from iovecs */ - tmp = size; - for (off = i = 0; tmp > 0 && i < msgP->msg_iovlen; i++) { - if (msgP->msg_iov[i].iov_len > 0) { - if (tmp > msgP->msg_iov[i].iov_len) - n = msgP->msg_iov[i].iov_len; - else - n = tmp; - memcpy(&sbuf[off], msgP->msg_iov[i].iov_base, n); - off += n; - tmp -= n; - } - } - } + char buf[RX_MAX_PACKET_SIZE]; + char *sbuf = buf; + int size, tmp; + int off, i, n; + int allocd = 0; + + for (size = i = 0; i < msgP->msg_iovlen; i++) + size += msgP->msg_iov[i].iov_len; - code = - sendto((SOCKET) socket, sbuf, size, flags, - (struct sockaddr *)(msgP->msg_name), msgP->msg_namelen); - -#ifdef AFS_NT40_ENV - if (code == SOCKET_ERROR) { - code = WSAGetLastError(); - switch (code) { - case WSAEINPROGRESS: - case WSAENETRESET: - case WSAENOBUFS: - errno = 0; - break; - case WSAEWOULDBLOCK: - case WSAECONNRESET: - errno = WSAEWOULDBLOCK; - break; - case WSAEHOSTUNREACH: + if (msgP->msg_iovlen <= 2) { + sbuf = msgP->msg_iov[0].iov_base; + } else { + /* Pack data into array from iovecs */ + tmp = size; + for (off = i = 0; tmp > 0 && i < msgP->msg_iovlen; i++) { + if (msgP->msg_iov[i].iov_len > 0) { + if (tmp > msgP->msg_iov[i].iov_len) + n = msgP->msg_iov[i].iov_len; + else + n = tmp; + memcpy(&sbuf[off], msgP->msg_iov[i].iov_base, n); + off += n; + tmp -= n; + } + } + } + + code = + sendto((SOCKET) socket, sbuf, size, flags, + (struct sockaddr *)(msgP->msg_name), msgP->msg_namelen); + if (code == SOCKET_ERROR) { + code = WSAGetLastError(); + switch (code) { + case WSAEINPROGRESS: + case WSAENETRESET: + case WSAENOBUFS: + errno = 0; + break; + case WSAEWOULDBLOCK: + case WSAECONNRESET: + errno = WSAEWOULDBLOCK; + break; + case WSAEHOSTUNREACH: errno = WSAEHOSTUNREACH; break; - default: - errno = EIO; - break; - } - code = -1; - } else -#endif /* AFS_NT40_ENV */ - - if (code < size) { - errno = EIO; - code = -1; + default: + errno = EIO; + break; + } + code = -1; + } else { + if (code < size) { + errno = EIO; + code = -1; + } + } } - return code; } diff --git a/src/rx/rx_xmit_nt.h b/src/rx/rx_xmit_nt.h index 7ffc26e83..ef5552d95 100644 --- a/src/rx/rx_xmit_nt.h +++ b/src/rx/rx_xmit_nt.h @@ -10,11 +10,10 @@ #ifndef _RX_XMIT_NT_H_ #define _RX_XMIT_NT_H_ - typedef struct iovec { - void *iov_base; - int iov_len; + unsigned long iov_len; + char *iov_base; } iovec_t; @@ -31,4 +30,6 @@ extern int rxi_sendmsg(osi_socket socket, struct msghdr *msgP, int flags); #define sendmsg rxi_sendmsg extern int rxi_recvmsg(osi_socket socket, struct msghdr *msgP, int flags); #define recvmsg rxi_recvmsg + +extern void rxi_xmit_init(osi_socket socket); #endif /* _RX_XMIT_NT_H_ */ -- 2.39.5