DnsQuery 与 DnsQueryEx - 提高性能和使用率或替代方案
我正在尝试在 Windows 上对 IPv4/6 使用反向 dns 查找(如 spamhaus 等反垃圾邮件)。到目前为止,结果令人失望。
我的要求是:
- 能够查找 IPv4 和 IPV6
- 使用自定义 DNS 服务器,例如 1.1.1.1、1.0.0.1、 2606:4700:4700::1111、2606:4700:4700::1001(添加超过1)
- 可接受的查找性能
这是我迄今为止发现的:
- DnsQuery_A/W 速度很快,但不支持 IPv6 DNS
- DnsQueryEx 支持 IPv6 DNS,但速度较慢比 DnsQuery_A/W 至少在我使用同步模式的测试中(我确实注意到使用异步模式的性能明显更快,但是我无法在每个 IP 的循环内正确“等待”它)
- GetAddrInfoExW 的性能很糟糕,所以甚至不打算谈论它
以下是迭代 73 IP 黑名单 DNS,在发布和默认优化下:
- DnsQuery_W:11 秒
- DnsQueryEx:24 秒
此测试重复了几次,以确保粗略的计时。无论如何,DnsQuery_W 都是赢家,但是它不支持 IPv6 DNS。此外,没有文档说明如何将 1 个以上的 DNS 添加到阵列中。
当然,我确实知道 DNS 服务器有时回复速度会较慢;然而20秒是很长的时间……太长了。
示例代码 DnsQuery_W:
PDNS_RECORD pDnsRecord = { 0 };
// Calling function DnsQuery to query Host or PTR records
DNS_STATUS status = DnsQuery_W(temp.c_str(), //Pointer to OwnerName.
DNS_TYPE_A, //Type of the record to be queried.
DNS_QUERY_BYPASS_CACHE, // Bypasses the resolver cache on the lookup.
pSrvList, //Contains DNS server IP address.
&pDnsRecord, //Resource record that contains the response.
NULL); //Reserved for future use.
if (status)
{
wprintf(L"Failed to query the host record for %ws and the error is %ws \n", temp.c_str(), GetErrorMessage(status).c_str());
}
else
{
wprintf(L"Found %ws in %ws and the error is %d \n", temp.c_str(), list.second.c_str(), status);
// Free memory allocated for DNS records.
DNS_FREE_TYPE freetype;
freetype = DnsFreeRecordListDeep;
DnsRecordListFree(pDnsRecord, freetype);
}
示例代码 DnsQueryEx:
SOCKADDR_STORAGE SockAddr = { 0 };
INT AddressLength = sizeof(SockAddr);
WSAStringToAddressW((PWSTR)L"1.1.1.1", AF_INET, NULL, (LPSOCKADDR)&SockAddr, &AddressLength);
DNS_ADDR_ARRAY DnsServerList = { 0 };
DnsServerList.MaxCount = 1;
DnsServerList.AddrCount = 1;
CopyMemory(DnsServerList.AddrArray[0].MaxSa, &SockAddr, DNS_ADDR_MAX_SOCKADDR_LENGTH);
PDNS_QUERY_CONTEXT pDnsQueryContext = NULL;
pDnsQueryContext = (PDNS_QUERY_CONTEXT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DNS_QUERY_CONTEXT));
if (NULL == pDnsQueryContext) {
std::wcout << L"HeapAlloc() failed with error: " << GetErrorMessage(GetLastError()).c_str();
continue;
}
pDnsQueryContext->QueryType = DNS_TYPE_A;
pDnsQueryContext->QueryResult.Version = DNS_QUERY_REQUEST_VERSION1;
pDnsQueryContext->Callback = NULL;
DNS_QUERY_REQUEST DnsQueryRequest = { 0 };
DnsQueryRequest.Version = DNS_QUERY_REQUEST_VERSION1;
DnsQueryRequest.QueryName = temp.c_str();
DnsQueryRequest.QueryType = pDnsQueryContext->QueryType;
DnsQueryRequest.QueryOptions = DNS_QUERY_BYPASS_CACHE;
DnsQueryRequest.pDnsServerList = &DnsServerList;
DnsQueryRequest.InterfaceIndex = 0;
// By omitting the DNS_QUERY_COMPLETION_ROUTINE callback from the pQueryCompleteCallback member of this structure, DnsQueryEx is called synchronously.
DnsQueryRequest.pQueryCompletionCallback = NULL;
DnsQueryRequest.pQueryContext = pDnsQueryContext;
auto start = std::chrono::high_resolution_clock::now();
DNS_STATUS DnsStatus = DnsQueryEx(&DnsQueryRequest, &pDnsQueryContext->QueryResult, &pDnsQueryContext->DnsCancelHandle);
auto stop = std::chrono::high_resolution_clock::now();
std::wcout << L"DnsStatus: " << DnsStatus << L" (" << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() << L"ms) -> " << GetErrorMessage(DnsStatus).c_str();
pDnsQueryContext->QueryResult.QueryStatus = DnsStatus;
if (pDnsQueryContext->QueryResult.QueryStatus != ERROR_SUCCESS)
{
if (NULL != pDnsQueryContext->QueryResult.pQueryRecords) {
DnsRecordListFree(pDnsQueryContext->QueryResult.pQueryRecords, DnsFreeRecordList);
}
HeapFree(GetProcessHeap(), NULL, pDnsQueryContext);
continue;
}
for (PDNS_RECORD p = pDnsQueryContext->QueryResult.pQueryRecords; p; p = p->pNext)
{
WCHAR ipAddress[128] = {0};
switch (p->wType)
{
case DNS_TYPE_A:
{
IN_ADDR ipv4;
ipv4.S_un.S_addr = p->Data.A.IpAddress;
RtlIpv4AddressToStringW(&ipv4, ipAddress);
}
break;
case DNS_TYPE_AAAA:
{
IN6_ADDR ipv6;
memcpy(ipv6.u.Byte, p->Data.AAAA.Ip6Address.IP6Byte, sizeof(ipv6.u.Byte));
RtlIpv6AddressToStringW(&ipv6, ipAddress);
}
break;
default:
break;
}
std::wcout << L"Found IP: " << ipAddress << L" in DNS: " << temp.c_str() << std::endl;
}
DnsRecordListFree(pDnsQueryContext->QueryResult.pQueryRecords, DnsFreeRecordList);
HeapFree(GetProcessHeap(), NULL, pDnsQueryContext);
有人可以建议如何实现我的目标吗?
如果有任何开箱即用的东西可以正常工作,我很乐意使用任何 C++ 库,例如 Boost 等。
另外,如果有人可以向我展示如何在向量循环内“等待每个结果”,我将非常乐意使用 DnsQueryEx 的异步方法。
非常感谢!
I am trying to use reverse dns lookup (antispam like spamhaus) for IPv4/6 on Windows. So far the results are more than disappointing.
My requirement would be:
- Ability to lookup both IPv4 and IPV6
- Use custom DNS servers like 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001 (add more than 1)
- Acceptable performance for lookup
Here is what I found so far:
- DnsQuery_A/W is fast-ish, but does not support IPv6 DNS
- DnsQueryEx supports IPv6 DNS, but is slower than DnsQuery_A/W, at least in my tests using synchronous mode (I did notice significant faster performance using asynchronous mode, however I am unable to "wait" for it properly inside a loop for each IP)
- GetAddrInfoExW is just terrible in performance, so not even going to talk about it
And here are some results from iterating a simple vector of 73 IP blacklist DNS, under Release and default optimizations:
- DnsQuery_W: 11 seconds
- DnsQueryEx: 24 seconds
This test was repeated several times to ensure a rough timing. DnsQuery_W is the winner in any case, however this does not support IPv6 DNS. Furthermore, there is no documentation how to add more than 1 DNS into the array.
Of course, I do understand that DNS servers can reply sometimes slower; however 20 seconds is a long time... too long.
Sample code DnsQuery_W:
PDNS_RECORD pDnsRecord = { 0 };
// Calling function DnsQuery to query Host or PTR records
DNS_STATUS status = DnsQuery_W(temp.c_str(), //Pointer to OwnerName.
DNS_TYPE_A, //Type of the record to be queried.
DNS_QUERY_BYPASS_CACHE, // Bypasses the resolver cache on the lookup.
pSrvList, //Contains DNS server IP address.
&pDnsRecord, //Resource record that contains the response.
NULL); //Reserved for future use.
if (status)
{
wprintf(L"Failed to query the host record for %ws and the error is %ws \n", temp.c_str(), GetErrorMessage(status).c_str());
}
else
{
wprintf(L"Found %ws in %ws and the error is %d \n", temp.c_str(), list.second.c_str(), status);
// Free memory allocated for DNS records.
DNS_FREE_TYPE freetype;
freetype = DnsFreeRecordListDeep;
DnsRecordListFree(pDnsRecord, freetype);
}
Sample code DnsQueryEx:
SOCKADDR_STORAGE SockAddr = { 0 };
INT AddressLength = sizeof(SockAddr);
WSAStringToAddressW((PWSTR)L"1.1.1.1", AF_INET, NULL, (LPSOCKADDR)&SockAddr, &AddressLength);
DNS_ADDR_ARRAY DnsServerList = { 0 };
DnsServerList.MaxCount = 1;
DnsServerList.AddrCount = 1;
CopyMemory(DnsServerList.AddrArray[0].MaxSa, &SockAddr, DNS_ADDR_MAX_SOCKADDR_LENGTH);
PDNS_QUERY_CONTEXT pDnsQueryContext = NULL;
pDnsQueryContext = (PDNS_QUERY_CONTEXT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DNS_QUERY_CONTEXT));
if (NULL == pDnsQueryContext) {
std::wcout << L"HeapAlloc() failed with error: " << GetErrorMessage(GetLastError()).c_str();
continue;
}
pDnsQueryContext->QueryType = DNS_TYPE_A;
pDnsQueryContext->QueryResult.Version = DNS_QUERY_REQUEST_VERSION1;
pDnsQueryContext->Callback = NULL;
DNS_QUERY_REQUEST DnsQueryRequest = { 0 };
DnsQueryRequest.Version = DNS_QUERY_REQUEST_VERSION1;
DnsQueryRequest.QueryName = temp.c_str();
DnsQueryRequest.QueryType = pDnsQueryContext->QueryType;
DnsQueryRequest.QueryOptions = DNS_QUERY_BYPASS_CACHE;
DnsQueryRequest.pDnsServerList = &DnsServerList;
DnsQueryRequest.InterfaceIndex = 0;
// By omitting the DNS_QUERY_COMPLETION_ROUTINE callback from the pQueryCompleteCallback member of this structure, DnsQueryEx is called synchronously.
DnsQueryRequest.pQueryCompletionCallback = NULL;
DnsQueryRequest.pQueryContext = pDnsQueryContext;
auto start = std::chrono::high_resolution_clock::now();
DNS_STATUS DnsStatus = DnsQueryEx(&DnsQueryRequest, &pDnsQueryContext->QueryResult, &pDnsQueryContext->DnsCancelHandle);
auto stop = std::chrono::high_resolution_clock::now();
std::wcout << L"DnsStatus: " << DnsStatus << L" (" << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() << L"ms) -> " << GetErrorMessage(DnsStatus).c_str();
pDnsQueryContext->QueryResult.QueryStatus = DnsStatus;
if (pDnsQueryContext->QueryResult.QueryStatus != ERROR_SUCCESS)
{
if (NULL != pDnsQueryContext->QueryResult.pQueryRecords) {
DnsRecordListFree(pDnsQueryContext->QueryResult.pQueryRecords, DnsFreeRecordList);
}
HeapFree(GetProcessHeap(), NULL, pDnsQueryContext);
continue;
}
for (PDNS_RECORD p = pDnsQueryContext->QueryResult.pQueryRecords; p; p = p->pNext)
{
WCHAR ipAddress[128] = {0};
switch (p->wType)
{
case DNS_TYPE_A:
{
IN_ADDR ipv4;
ipv4.S_un.S_addr = p->Data.A.IpAddress;
RtlIpv4AddressToStringW(&ipv4, ipAddress);
}
break;
case DNS_TYPE_AAAA:
{
IN6_ADDR ipv6;
memcpy(ipv6.u.Byte, p->Data.AAAA.Ip6Address.IP6Byte, sizeof(ipv6.u.Byte));
RtlIpv6AddressToStringW(&ipv6, ipAddress);
}
break;
default:
break;
}
std::wcout << L"Found IP: " << ipAddress << L" in DNS: " << temp.c_str() << std::endl;
}
DnsRecordListFree(pDnsQueryContext->QueryResult.pQueryRecords, DnsFreeRecordList);
HeapFree(GetProcessHeap(), NULL, pDnsQueryContext);
Can someone please advise on how to achieve my goal(s)?
I'm happy to use any C++ libraries like Boost, etc, if there is anything out of the box that works decently.
Also, I would be more than happy to use the async method of DnsQueryEx, if someone can show me how to "wait for each result" inside a vector loop.
Much appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果你还有兴趣的话,这是我的经验。我使用 DnsQuery_W,速度足够快,无需异步,而且不像 DnsQueryEx 那么复杂。您也可以指定多个 DNS 服务器地址并使用 IPv6。您肯定已经安装了 Powershell。如果您使用 ILSpy 或 .NET Reflector 查看已安装模块中的“dnslookup.dll”文件,您会在“ResolveDnsName”下找到如何使用 DnsQuery_W 的一个很好的示例。它是Powershell模块“Resolve-DnsName”的源代码。反向查询比 GetHostEntry 快得多。
这是一个不那么简短的总结。我仅将 DnsQuery 用于反向查询,因此这是一个非常精简的示例。
您可以将其用于:
通过 Wireshark 或防火墙,您可以看到它适用于 IPv6,并且 DnsQuery 使用服务器地址。我不知道DnsQuery中是否存储了对5个地址的限制。如果相应地调整结构,也许可以增加数量。玩得开心!
If you're still interested, here's my experience. I use DnsQuery_W, fast enough to do without asynchronous and not as complicated as DnsQueryEx. And you can very well specify more than one DNS server address and use IPv6 too. You certainly have Powershell installed. If you use ILSpy or .NET Reflector to look at the "dnslookup.dll" file in the installed modules, you'll find a nice example of how to use DnsQuery_W under "ResolveDnsName". It is the source code for the Powershell module "Resolve-DnsName". Extremely faster than GetHostEntry for reverse queries.
Here is a - not so short - summary. I use DnsQuery only for reverse queries, so this is a really stripped down sample.
You can use it with:
With Wireshark or a firewall you can see that it works with IPv6 and that DnsQuery uses the server addresses. I don't know if the restriction to 5 addresses is stored in DnsQuery. Maybe you can increase the number if you adjust the structures accordingly. Have fun!