我有兴趣检索入站数据包发送到的目标地址。例如,在 Linux 上,您可以使用 recvmsg
:
res = recvmsg(socket, &msghdr, 0);
get_cmsg = CMSG_FIRSTHDR(msghdr);
struct in_pktinfo *get_pktinfo = (struct in_pktinfo *)CMSG_DATA(get_cmsg);
printf(" - Header destination address (get_pktinfo.ipi_addr)=%s\n", inet_ntoa(pktinfo.ipi_addr));
已跳过步骤以保存许多行
这里的关键是 recvmsg
易于使用。 Windows XP 也实现了类似的功能,如 recvfrom
但Windows似乎没有实现recvmsg
功能。
存在类似命名的函数,例如 WSARevcMsg
功能,但根据链接文档,它仅包含在 Windows Vista 及更高版本中。
有没有办法在 Windows XP 中从数据包中获取 header 目标地址?
我知道使用 XP 很糟糕而且很旧,不幸的是我们正在尝试修复旧产品上的错误,所以目前我们无法简单地升级。
最佳答案
windows does not seem to implement the recvmsg function.
事实上,确实如此(嗯,无论如何,是等价的):
Similar functions have been revamped like the WSArevcMsg but these are only included in windows vista and above.
您不能总是相信 MSDN 文档。当微软正式放弃对 Windows 版本(如 XP)的支持时,它往往会从 MSDN 中删除对该版本的引用,包括 API 函数的“最低支持...”要求(这让许多程序员感到烦恼) 。关键点是支持。 Microsoft 不支持较旧的 Windows 版本。
WSARecvMsg()
首次在 XP 中引入,于 2014 年停产,MSDN 文档中删除了许多对它的引用(这里是 2013 年的 proof,当时 WSARecvMsg ()
文档指出 XP 而不是 Vista 是“支持的最低客户端”。
如 WSARecvFrom()
文档中所述:
Note The function pointer for the
WSARecvMsg
function must be obtained at run time by making a call to theWSAIoctl()
function with theSIO_GET_EXTENSION_FUNCTION_POINTER
opcode specified. The input buffer passed to theWSAIoctl
function must containWSAID_WSARECVMSG
, a globally unique identifier (GUID) whose value identifies theWSARecvMsg
extension function. On success, the output returned by theWSAIoctl
function contains a pointer to theWSARecvMsg
function. TheWSAID_WSARECVMSG
GUID is defined in the Mswsock.h header file.
WSAID_WSARECVMSG
定义为:
#define WSAID_WSARECVMSG \
{0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
又名 {F689D7C8-6F1F-436B-8A53-E54FE351C322}
文本格式。
例如:
SOCKET sckt = ...;
LPFN_WSARECVMSG lpWSARecvMsg = NULL;
GUID g = WSAID_WSARECVMSG;
DWORD dwBytesReturned = 0;
if (WSAIoctl(sckt, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &dwBytesReturned, NULL, NULL) != 0)
{
// WSARecvMsg is not available...
}
然后,使用它:
BYTE buffer[...];
DWORD dwBytesRecv;
WSABUF msgbuf;
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.len = sizeof(buffer);
msgbuf.buf = (char *) buffer;
// call WSA_CMSG_SPACE() once for each option you enable
// on the socket that can return data in WSARecvMsg()...
int size = 0;
if (... IP_PKTINFO is enabled ...)
size += WSA_CMSG_SPACE(sizeof(struct in_pktinfo));
if (... IPV6_PKTINFO is enabled ...)
size += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo));
// other packet options as needed...
BYTE *controlbuf = (BYTE *) malloc(size);
SOCKADDR_STORAGE *addrbuf = (SOCKADDR_STORAGE *) malloc(sizeof(SOCKADDR_STORAGE));
WSAMSG msg;
memset(&msg, 0, sizeof(msg));
msg.name = (struct sockaddr *) addrbuf;
msg.namelen = sizeof(SOCKADDR_STORAGE);
msg.lpBuffers = &msgbuf;
msg.dwBufferCount = 1;
msg.Control.len = size;
msg.Control.buf = (char *) controlbuf;
if (lpWSARecvMsg(sckt, &msg, &dwBytesRecv, NULL, NULL) == 0)
{
// addrbuf contains the sender's IP and port...
switch (addrbuf->ss_family)
{
case AF_INET:
{
struct sockaddr_in *addr = (struct sockaddr_in*) addrbuf;
// use addr as needed...
break;
}
case AF_INET6:
{
struct sockaddr_in6 *addr = (struct sockaddr_in6*) addrbuf;
// use addr as needed...
break;
}
}
WSACMSGHDR *msghdr = WSA_CMSG_FIRSTHDR(&msg);
while (msghdr)
{
switch (msghdr->cmsg_type)
{
case IP_PKTINFO: // also IPV6_PKTINF
{
// must call setsockopt(sckt, IPPROTO_IP, IP_PKTINFO, TRUE) beforehand to receive this for IPv4
// must call setsockopt(sckt, IPPROTO_IPV6, IPV6_PKTINFO, TRUE) beforehand to receive this for IPv6
switch (addrbuf->ss_family)
{
case AF_INET:
{
struct in_pktinfo *pktinfo = (struct in_pktinfo *) WSA_CMSG_DATA(msghdr);
// use pktinfo as needed...
break;
}
case AF_INET6:
{
struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) WSA_CMSG_DATA(msghdr);
// use pktinfo as needed...
break;
}
}
break;
}
// other packet options as needed...
}
msghdr = WSA_CMSG_NXTHDR(&msg, msghdr);
}
}
free(addrbuf);
free(controlbuf);
关于c++ - 在 Windows XP 中从数据包中检索 header 目标地址的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37334871/