Using WSARecvMsg


Click here to change the theme.

WSARecvMsg can be used instead of WSARecv and WSARecvFrom, but WSARecvMsg is only supported by XP and above. The advantage of WSARecvMsg is that it can provide some information that cannot be (easily?) obtained any other way. For IP version 4, the documentation I think says that The documentation (currently) has errors, but the following  works for me.

	GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
	LPFN_WSARECVMSG WSARecvMsg;
	char ControlBuffer[1024];
	DWORD NumberOfBytes;
	string ErrorMessage, Address;
	WSABUF WSABuf;
	WSAMSG Msg;
	int nResult;
nResult = WSAIoctl(m_Socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
		 &WSARecvMsg_GUID, sizeof WSARecvMsg_GUID,
		 &WSARecvMsg, sizeof WSARecvMsg,
		 &NumberOfBytes, NULL, NULL);
if (nResult == SOCKET_ERROR) {
	m_ErrorCode = WSAGetLastError();
	WSARecvMsg = NULL;
	return 0;
	}
Msg.name = SocketAddress;
Msg.namelen = sizeof sockaddr_in;
WSABuf.buf = (char *)Buffer;
WSABuf.len = Length;
Msg.lpBuffers = &WSABuf;
Msg.dwBufferCount = 1;
Msg.Control.len = sizeof ControlBuffer;
Msg.Control.buf = ControlBuffer;
Msg.dwFlags = nFlags;
nResult = WSARecvMsg(m_Socket, &Msg, &NumberOfBytes, NULL, NULL);
if (nResult == SOCKET_ERROR) {
	m_ErrorCode = WSAGetLastError();
	return 0;
	}
// There can be multiple enties but the following will show only the first.
WSACMSGHDR *pCMsgHdr = WSA_CMSG_FIRSTHDR(&Msg);
if (pCMsgHdr)
	switch (pCMsgHdr->cmsg_type) {
		case IP_PKTINFO: {
				IN_PKTINFO *pPktInfo;
				CSocketAddressIn DestinationAddress;
				pPktInfo = (IN_PKTINFO *)WSA_CMSG_DATA(pCMsgHdr);
				DestinationAddress.SetHostAddress(pPktInfo->ipi_addr.S_un.S_addr);
				DestinationAddress.GetAddress(Address);
				cout << "Destination address: " << Address
					<< ", interface index: " << pPktInfo->ipi_ifindex << '\n';
				}
			break;
		default:
			cout << "Unknown message type: " << pCMsgHdr->cmsg_type
				<< "; level: " << pCMsgHdr->cmsg_level << '\n';
			break;
		}

Where:

SocketAddress
is an instance of the CSocketAddressIn class
Buffer, Length, and nFlags
are the same as for recvfrom and WSARecv.

The option requesting data must be set. Put the following after creating the socket (calling socket).

	int nResult, optval(1);
nResult = setsockopt(m_Socket, IPPROTO_IP, IP_PKTINFO, (char*)&optval, sizeof(int));

Sample Project

Download UDPio.zip for a sample project using the code above. It is a console program and is not the best-written, but it should be enough to show use of WSARecvMsg.