ws2_32: Add support for WSARecvMsg and IP_PKTINFO.

This commit is contained in:
Erich Hoover 2010-12-05 17:08:54 -07:00 committed by Alexandre Julliard
parent 39b260e8be
commit 54b4f836fd
1 changed files with 159 additions and 22 deletions

View File

@ -171,9 +171,9 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
/*
* The actual definition of WSASendTo/WSARecvFrom, wrapped in a different
* function name, so that internal calls from ws2_32 itself will not trigger
* programs like Garena, which hooks WSASendTo/WSARecvFrom calls.
* The actual definition of WSASendTo, wrapped in a different function name
* so that internal calls from ws2_32 itself will not trigger programs like
* Garena, which hooks WSASendTo/WSARecvFrom calls.
*/
static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
@ -181,11 +181,16 @@ static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
static int WS2_recvfrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,
struct WS_sockaddr *lpFrom,
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
/*
* Internal fundamental receive function, essentially WSARecvFrom with an
* additional parameter to support message control headers.
*/
static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,
struct WS_sockaddr *lpFrom,
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSABUF lpControlBuffer );
/* critical section to protect some non-reentrant net function */
static CRITICAL_SECTION csWSgetXXXbyYYY;
@ -261,6 +266,8 @@ typedef struct ws2_async
int *ptr; /* for recv operations */
} addrlen;
DWORD flags;
DWORD *lpFlags;
WSABUF *control;
unsigned int n_iovecs;
unsigned int first_iovec;
struct iovec iovec[1];
@ -375,6 +382,9 @@ static const int ws_ip_map[][2] =
#endif
MAP_OPTION( IP_TOS ),
MAP_OPTION( IP_TTL ),
#ifdef IP_PKTINFO
MAP_OPTION( IP_PKTINFO ),
#endif
};
static const int ws_ipv6_map[][2] =
@ -468,6 +478,75 @@ static const int ws_eai_map[][2] =
static const char magic_loopback_addr[] = {127, 12, 34, 56};
#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
static inline WSACMSGHDR *fill_control_message(int level, int type, WSACMSGHDR *current, ULONG *maxsize, void *data, int len)
{
ULONG msgsize = sizeof(WSACMSGHDR) + WSA_CMSG_ALIGN(len);
char *ptr = (char *) current + sizeof(WSACMSGHDR);
/* Make sure there is at least enough room for this entry */
if (msgsize > *maxsize)
return NULL;
*maxsize -= msgsize;
/* Fill in the entry */
current->cmsg_len = sizeof(WSACMSGHDR) + len;
current->cmsg_level = level;
current->cmsg_type = type;
memcpy(ptr, data, len);
/* Return the pointer to where next entry should go */
return (WSACMSGHDR *) (ptr + WSA_CMSG_ALIGN(len));
}
static inline int convert_control_headers(struct msghdr *hdr, WSABUF *control)
{
#ifdef IP_PKTINFO
WSACMSGHDR *cmsg_win = (WSACMSGHDR *) control->buf, *ptr;
ULONG ctlsize = control->len;
struct cmsghdr *cmsg_unix;
ptr = cmsg_win;
/* Loop over all the headers, converting as appropriate */
for (cmsg_unix = CMSG_FIRSTHDR(hdr); cmsg_unix != NULL; cmsg_unix = CMSG_NXTHDR(hdr, cmsg_unix))
{
switch(cmsg_unix->cmsg_level)
{
case IPPROTO_IP:
switch(cmsg_unix->cmsg_type)
{
case IP_PKTINFO:
{
/* Convert the Unix IP_PKTINFO structure to the Windows version */
struct in_pktinfo *data_unix = (struct in_pktinfo *) CMSG_DATA(cmsg_unix);
struct WS_in_pktinfo data_win;
memcpy(&data_win.ipi_addr,&data_unix->ipi_addr.s_addr,4); /* 4 bytes = 32 address bits */
data_win.ipi_ifindex = data_unix->ipi_ifindex;
ptr = fill_control_message(WS_IPPROTO_IP, WS_IP_PKTINFO, ptr, &ctlsize,
(void*)&data_win, sizeof(data_win));
if (!ptr) goto error;
} break;
default:
FIXME("Unhandled IPPROTO_IP message header type %d\n", cmsg_unix->cmsg_type);
break;
}
break;
default:
FIXME("Unhandled message header level %d\n", cmsg_unix->cmsg_level);
break;
}
}
error:
/* Set the length of the returned control headers */
control->len = (ptr == NULL ? 0 : (char*)ptr - (char*)cmsg_win);
return (ptr != NULL);
#else /* IP_PKTINFO */
control->len = 0;
return 1;
#endif /* IP_PKTINFO */
}
#endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */
/* ----------------------------------- error handling */
static NTSTATUS sock_get_ntstatus( int err )
@ -1411,6 +1490,9 @@ static void WINAPI ws2_async_apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserv
*/
static int WS2_recv( int fd, struct ws2_async *wsa )
{
#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
char pktbuf[512];
#endif
struct msghdr hdr;
union generic_unix_sockaddr unix_sockaddr;
int n;
@ -1431,14 +1513,30 @@ static int WS2_recv( int fd, struct ws2_async *wsa )
hdr.msg_accrights = NULL;
hdr.msg_accrightslen = 0;
#else
hdr.msg_control = NULL;
hdr.msg_controllen = 0;
hdr.msg_control = pktbuf;
hdr.msg_controllen = sizeof(pktbuf);
hdr.msg_flags = 0;
#endif
if ( (n = recvmsg(fd, &hdr, wsa->flags)) == -1 )
return -1;
#ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
if (wsa->control)
{
ERR("Message control headers cannot be properly supported on this system.\n");
control->len = 0;
}
#else
if (wsa->control && !convert_control_headers(&hdr, wsa->control))
{
WARN("Application passed insufficient room for control headers.\n");
*wsa->lpFlags |= WS_MSG_CTRUNC;
errno = EMSGSIZE;
return -1;
}
#endif
/* if this socket is connected and lpFrom is not NULL, Linux doesn't give us
* msg_name and msg_namelen from recvmsg, but it does set msg_namelen to zero.
*
@ -1914,8 +2012,10 @@ static BOOL WINAPI WS2_AcceptEx(SOCKET listener, SOCKET acceptor, PVOID dest, DW
wsa->read->hSocket = wsa->accept_socket;
wsa->read->flags = 0;
wsa->read->lpFlags = &wsa->read->flags;
wsa->read->addr = NULL;
wsa->read->addrlen.ptr = NULL;
wsa->read->control = NULL;
wsa->read->n_iovecs = 1;
wsa->read->first_iovec = 0;
wsa->read->iovec[0].iov_base = wsa->buf;
@ -1961,6 +2061,29 @@ static void WINAPI WS2_GetAcceptExSockaddrs(PVOID buffer, DWORD data_size, DWORD
*remote_addr = (struct WS_sockaddr *)(cbuf + sizeof(int));
}
/***********************************************************************
* WSARecvMsg
*
* Perform a receive operation that is capable of returning message
* control headers. It is important to note that the WSAMSG parameter
* must remain valid throughout the operation, even when an overlapped
* receive is performed.
*/
static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfBytesRecvd,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
{
if (!msg)
{
SetLastError( WSAEFAULT );
return SOCKET_ERROR;
}
return WS2_recv_base( s, msg->lpBuffers, msg->dwBufferCount, lpNumberOfBytesRecvd,
&msg->dwFlags, msg->name, &msg->namelen,
lpOverlapped, lpCompletionRoutine, &msg->Control );
}
/***********************************************************************
* bind (WS2_32.2)
*/
@ -2213,6 +2336,8 @@ static BOOL WINAPI WS2_ConnectEx(SOCKET s, const struct WS_sockaddr* name, int n
wsa->addr = NULL;
wsa->addrlen.val = 0;
wsa->flags = 0;
wsa->lpFlags = &wsa->flags;
wsa->control = NULL;
wsa->n_iovecs = sendBuf ? 1 : 0;
wsa->first_iovec = 0;
wsa->completion_func = NULL;
@ -2674,6 +2799,9 @@ INT WINAPI WS_getsockopt(SOCKET s, INT level,
case WS_IP_MULTICAST_LOOP:
case WS_IP_MULTICAST_TTL:
case WS_IP_OPTIONS:
#ifdef IP_PKTINFO
case WS_IP_PKTINFO:
#endif
case WS_IP_TOS:
case WS_IP_TTL:
if ( (fd = get_sock_fd( s, 0, NULL )) == -1)
@ -3199,7 +3327,8 @@ INT WINAPI WSAIoctl(SOCKET s,
}
else if ( IsEqualGUID(&wsarecvmsg_guid, lpvInBuffer) )
{
FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented WSARecvMsg\n");
*(LPFN_WSARECVMSG *)lpbOutBuffer = WS2_WSARecvMsg;
return 0;
}
else if ( IsEqualGUID(&wsasendmsg_guid, lpvInBuffer) )
{
@ -3459,7 +3588,7 @@ int WINAPI WS_recv(SOCKET s, char *buf, int len, int flags)
wsabuf.len = len;
wsabuf.buf = buf;
if ( WS2_recvfrom(s, &wsabuf, 1, &n, &dwFlags, NULL, NULL, NULL, NULL) == SOCKET_ERROR )
if ( WS2_recv_base(s, &wsabuf, 1, &n, &dwFlags, NULL, NULL, NULL, NULL, NULL) == SOCKET_ERROR )
return SOCKET_ERROR;
else
return n;
@ -3477,7 +3606,7 @@ int WINAPI WS_recvfrom(SOCKET s, char *buf, INT len, int flags,
wsabuf.len = len;
wsabuf.buf = buf;
if ( WS2_recvfrom(s, &wsabuf, 1, &n, &dwFlags, from, fromlen, NULL, NULL) == SOCKET_ERROR )
if ( WS2_recv_base(s, &wsabuf, 1, &n, &dwFlags, from, fromlen, NULL, NULL, NULL) == SOCKET_ERROR )
return SOCKET_ERROR;
else
return n;
@ -3743,6 +3872,8 @@ static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
wsa->addr = (struct WS_sockaddr *)to;
wsa->addrlen.val = tolen;
wsa->flags = dwFlags;
wsa->lpFlags = &wsa->flags;
wsa->control = NULL;
wsa->n_iovecs = dwBufferCount;
wsa->first_iovec = 0;
for ( i = 0; i < dwBufferCount; i++ )
@ -4139,6 +4270,9 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
case WS_IP_MULTICAST_LOOP:
case WS_IP_MULTICAST_TTL:
case WS_IP_OPTIONS:
#ifdef IP_PKTINFO
case WS_IP_PKTINFO:
#endif
case WS_IP_TOS:
case WS_IP_TTL:
convert_sockopt(&level, &optname);
@ -5514,15 +5648,16 @@ int WINAPI WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
return WS2_recvfrom(s, lpBuffers, dwBufferCount, NumberOfBytesReceived, lpFlags,
NULL, NULL, lpOverlapped, lpCompletionRoutine);
return WS2_recv_base(s, lpBuffers, dwBufferCount, NumberOfBytesReceived, lpFlags,
NULL, NULL, lpOverlapped, lpCompletionRoutine, NULL);
}
static int WS2_recvfrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct WS_sockaddr *lpFrom,
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,
struct WS_sockaddr *lpFrom,
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSABUF lpControlBuffer )
{
unsigned int i, options;
int n, fd, err;
@ -5548,8 +5683,10 @@ static int WS2_recvfrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
wsa->hSocket = SOCKET2HANDLE(s);
wsa->flags = *lpFlags;
wsa->lpFlags = lpFlags;
wsa->addr = lpFrom;
wsa->addrlen.ptr = lpFromlen;
wsa->control = lpControlBuffer;
wsa->n_iovecs = dwBufferCount;
wsa->first_iovec = 0;
for (i = 0; i < dwBufferCount; i++)
@ -5683,10 +5820,10 @@ INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
{
return WS2_recvfrom( s, lpBuffers, dwBufferCount,
return WS2_recv_base( s, lpBuffers, dwBufferCount,
lpNumberOfBytesRecvd, lpFlags,
lpFrom, lpFromlen,
lpOverlapped, lpCompletionRoutine );
lpOverlapped, lpCompletionRoutine, NULL );
}
/***********************************************************************