我有兴趣检索发送入站数据包的目标地址。例如,在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中从数据包中获取标头目标地址?
我知道使用XP很糟糕,遗憾的是我们正在尝试修复旧版产品的错误,所以目前我们无法简单升级。
答案 0 :(得分:2)
windows似乎没有实现recvmsg函数。
实际上,确实如此(好吧,等等):
类似的功能已经像WSArevcMsg一样进行了改进,但这些功能仅包含在windows vista及更高版本中。
您无法始终信任MSDN文档。当Microsoft正式取消对Windows版本(如XP)的支持时,它往往会从MSDN中删除对该版本的引用,包括“支持的最低......” API函数的要求(这会使许多程序员烦恼) 。关键点是支持。 Microsoft不支持旧的Windows版本。
WSARecvMsg()
最初是在XP中引入的,它在2014年进行了EOL,并且许多对它的引用都受到了MSDN文档的影响(从{2013年WSARecvMsg()
文档开始,这里是proof XP而不是Vista是“最低支持的客户端”。)
如WSARecvFrom()
文档中所述:
注意必须在运行时通过调用指定了
WSARecvMsg
操作码的WSAIoctl()
函数来获取SIO_GET_EXTENSION_FUNCTION_POINTER
函数的函数指针。传递给WSAIoctl
函数的输入缓冲区必须包含WSAID_WSARECVMSG
,这是一个全局唯一标识符(GUID),其值标识WSARecvMsg
扩展函数。成功时,WSAIoctl
函数返回的输出包含指向WSARecvMsg
函数的指针。WSAID_WSARECVMSG
GUID在 Mswsock.h 头文件中定义。
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_LEN() once for each option you enable
// on the socket that can return data in WSARecvMsg()...
int size = WSA_CMSG_LEN(WSA_CMSG_LEN(sizeof(buffer)));
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 = NULL;
do
{
msghdr = WSA_CMSG_NXTHDR(&msg, msghdr);
if (!msghdr) break;
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...
}
}
while (true);
}
free(addrbuf);
free(controlbuf);