用于从Windows XP中的数据包检索标头目标地址的函数

时间:2016-05-19 22:10:05

标签: c++ windows sockets

我有兴趣检索发送入站数据包的目标地址。例如,在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很糟糕,遗憾的是我们正在尝试修复旧版产品的错误,所以目前我们无法简单升级。

1 个答案:

答案 0 :(得分:2)

  

windows似乎没有实现recvmsg函数。

实际上,确实如此(好吧,等等):

WSARecvMsg function

  

类似的功能已经像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);