c++ - 在 Windows XP 中从数据包中检索 header 目标地址的函数

标签 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 中从数据包中获取 header 目标地址?


我知道使用 XP 很糟糕而且很旧,不幸的是我们正在尝试修复旧产品上的错误,所以目前我们无法简单地升级。

最佳答案

windows does not seem to implement the recvmsg function.

事实上,确实如此(嗯,无论如何,是等价的):

WSARecvMsg 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 the WSAIoctl() function with the SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified. The input buffer passed to the WSAIoctl function must contain WSAID_WSARECVMSG, a globally unique identifier (GUID) whose value identifies the WSARecvMsg extension function. On success, the output returned by the WSAIoctl function contains a pointer to the WSARecvMsg function. The WSAID_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/

相关文章:

C++堆和ifstream读取函数

c++ - 如何在 if 语句 C++ 中将字符串转换为较低的字符串

c++ - _pgmptr 发生了什么?

c# - 我如何访问 C :\Program Files\in c#

python - 如何在 python 中设置本地主机?

c++ - cv::merge 断言失败

c++ - 使用 std::string.c_str() 作为另一个方法的参数时的段错误

windows - 获取语​​言环境在命令行上执行时出现问题

java - ServerSocketChannel.open() 从未返回

c++ - read() 和 write() 到套接字返回损坏的值