2025-11-28 00:35:46 +09:00

365 lines
8.6 KiB
C++

//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// Abstract:
//
// The sample demonstrates how to use asynchronous DnsQueryEx to
// resolve DNS records as well as using various types of DNS records.
//
// DnsQuery -q <QueryName> [-t QueryType] [-o QueryOptions] [-s server]
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <windns.h>
#include <Ws2tcpip.h>
#include <Mstcpip.h>
//
// Asynchronous query context structure.
//
typedef struct _QueryContextStruct
{
ULONG RefCount;
WCHAR QueryName[DNS_MAX_NAME_BUFFER_LENGTH];
WORD QueryType;
ULONG QueryOptions;
DNS_QUERY_RESULT QueryResults;
DNS_QUERY_CANCEL QueryCancelContext;
HANDLE QueryCompletedEvent;
}QUERY_CONTEXT, *PQUERY_CONTEXT;
DWORD
ParseArguments(
_In_ int Argc,
_In_reads_(Argc) PWCHAR Argv[],
_Out_writes_(DNS_MAX_NAME_BUFFER_LENGTH) PWSTR QueryName,
_Out_ WORD *QueryType,
_Out_ ULONG *QueryOptions,
_Out_writes_(MAX_PATH) PWSTR ServerIp
);
VOID
PrintDnsRecordList(
_Inout_ PDNS_RECORD DnsRecord
);
VOID
AddReferenceQueryContext(
_Inout_ PQUERY_CONTEXT QueryContext
);
VOID
DeReferenceQueryContext(
_Inout_ PQUERY_CONTEXT *QueryContext
);
DWORD
AllocateQueryContext(
_Out_ PQUERY_CONTEXT *QueryContext
);
DWORD
CreateDnsServerList(
_In_ PWSTR ServerIp,
_Out_ PDNS_ADDR_ARRAY DnsServerList
);
VOID
WINAPI
QueryCompleteCallback(
_In_ PVOID Context,
_Inout_ PDNS_QUERY_RESULT QueryResults
);
int
__cdecl
wmain(
_In_ int Argc,
_In_reads_(Argc) PWCHAR Argv[]
)
{
DWORD Error = ERROR_SUCCESS;
PQUERY_CONTEXT QueryContext = NULL;
DNS_QUERY_REQUEST DnsQueryRequest;
DWORD QueryTimeout = 5 * 1000; // 5 seconds
WCHAR ServerIp[MAX_PATH];
DNS_ADDR_ARRAY DnsServerList;
//
// Allocate QueryContext
//
Error = AllocateQueryContext(&QueryContext);
if (Error != ERROR_SUCCESS)
{
wprintf(L"AllocateQueryContext failed with error %d", Error);
goto exit;
}
//
// Parse input arguments
//
Error = ParseArguments(Argc,
Argv,
QueryContext->QueryName,
&QueryContext->QueryType,
&QueryContext->QueryOptions,
ServerIp);
if (Error != ERROR_SUCCESS)
{
goto exit;
}
//
// Initiate asynchronous DnsQuery: Note that QueryResults and
// QueryCancelContext should be valid till query completes.
//
ZeroMemory(&DnsQueryRequest, sizeof(DnsQueryRequest));
DnsQueryRequest.Version = DNS_QUERY_REQUEST_VERSION1;
DnsQueryRequest.QueryName = QueryContext->QueryName;
DnsQueryRequest.QueryType = QueryContext->QueryType;
DnsQueryRequest.QueryOptions = (ULONG64)QueryContext->QueryOptions;
DnsQueryRequest.pQueryContext = QueryContext;
DnsQueryRequest.pQueryCompletionCallback = QueryCompleteCallback;
//
// If user specifies server, construct DNS_ADDR_ARRAY
//
if (ServerIp[0] != L'\0')
{
Error = CreateDnsServerList(ServerIp, &DnsServerList);
if (Error != ERROR_SUCCESS)
{
wprintf(L"CreateDnsServerList failed with error %d", Error);
goto exit;
}
DnsQueryRequest.pDnsServerList = &DnsServerList;
}
//
// Add a reference before initiating asynchronous query.
//
AddReferenceQueryContext(QueryContext);
Error = DnsQueryEx(&DnsQueryRequest,
&QueryContext->QueryResults,
&QueryContext->QueryCancelContext);
//
// If DnsQueryEx() returns DNS_REQUEST_PENDING, Completion routine
// will be invoked. If not (when completed inline) completion routine
// will not be invoked.
//
if (Error != DNS_REQUEST_PENDING)
{
QueryCompleteCallback(QueryContext, &QueryContext->QueryResults);
goto exit;
}
//
// Wait for query completion for 5 seconds and initiate cancel if
// completion has not happened.
//
if (WaitForSingleObject(QueryContext->QueryCompletedEvent,
QueryTimeout) == WAIT_TIMEOUT)
{
//
// Initiate Cancel: Note that Cancel is just a request which will
// speed the process. It should still wait for the completion callback.
//
wprintf(L"The query took longer than %d seconds to complete; "
L"cancelling the query...\n", QueryTimeout/1000);
DnsCancelQuery(&QueryContext->QueryCancelContext);
WaitForSingleObject(QueryContext->QueryCompletedEvent,
INFINITE);
}
exit:
if (QueryContext)
{
DeReferenceQueryContext(&QueryContext);
}
return Error;
}
VOID
AddReferenceQueryContext(
_Inout_ PQUERY_CONTEXT QueryContext
)
{
InterlockedIncrement(&QueryContext->RefCount);
}
VOID
DeReferenceQueryContext(
_Inout_ PQUERY_CONTEXT *QueryContext
)
{
PQUERY_CONTEXT QC = *QueryContext;
if (InterlockedDecrement(&QC->RefCount) == 0)
{
if (QC->QueryCompletedEvent)
{
CloseHandle(QC->QueryCompletedEvent);
}
HeapFree(GetProcessHeap(),
0,
QC);
*QueryContext = NULL;
}
}
DWORD
AllocateQueryContext(
_Out_ PQUERY_CONTEXT *QueryContext
)
{
DWORD Error = ERROR_SUCCESS;
*QueryContext = (PQUERY_CONTEXT) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(QUERY_CONTEXT));
if (*QueryContext == NULL)
{
Error = GetLastError();
goto exit;
}
AddReferenceQueryContext(*QueryContext);
(*QueryContext)->QueryResults.Version = DNS_QUERY_RESULTS_VERSION1;
(*QueryContext)->QueryCompletedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if ((*QueryContext)->QueryCompletedEvent == NULL)
{
Error = GetLastError();
DeReferenceQueryContext(QueryContext);
*QueryContext = NULL;
goto exit;
}
exit:
return Error;
}
//
// Wrapper function that creates DNS_ADDR_ARRAY from IP address string.
//
DWORD
CreateDnsServerList(
_In_ PWSTR ServerIp,
_Out_ PDNS_ADDR_ARRAY DnsServerList
)
{
DWORD Error = ERROR_SUCCESS;
SOCKADDR_STORAGE SockAddr;
INT AddressLength;
WSADATA wsaData;
ZeroMemory(DnsServerList, sizeof(*DnsServerList));
Error = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (Error != 0)
{
wprintf(L"WSAStartup failed with %d\n", Error);
return Error;
}
AddressLength = sizeof(SockAddr);
Error = WSAStringToAddress(ServerIp,
AF_INET,
NULL,
(LPSOCKADDR)&SockAddr,
&AddressLength);
if (Error != ERROR_SUCCESS)
{
AddressLength = sizeof(SockAddr);
Error = WSAStringToAddress(ServerIp,
AF_INET6,
NULL,
(LPSOCKADDR)&SockAddr,
&AddressLength);
}
if (Error != ERROR_SUCCESS)
{
wprintf(L"WSAStringToAddress for %s failed with error %d\n",
ServerIp,
Error);
goto exit;
}
DnsServerList->MaxCount = 1;
DnsServerList->AddrCount = 1;
CopyMemory(DnsServerList->AddrArray[0].MaxSa, &SockAddr, DNS_ADDR_MAX_SOCKADDR_LENGTH);
exit:
WSACleanup();
return Error;
}
//
// Callback function called by DNS as part of asynchronous query complete
//
VOID
WINAPI
QueryCompleteCallback(
_In_ PVOID Context,
_Inout_ PDNS_QUERY_RESULT QueryResults
)
{
PQUERY_CONTEXT QueryContext = (PQUERY_CONTEXT)Context;
if (QueryResults->QueryStatus == ERROR_SUCCESS)
{
wprintf(L"DnsQuery() succeeded.\n Query response records:\n");
PrintDnsRecordList(QueryResults->pQueryRecords);
}
else
{
wprintf(L"DnsQuery() failed, %d\n", QueryResults->QueryStatus);
}
if (QueryResults->pQueryRecords)
{
DnsRecordListFree(QueryResults->pQueryRecords, DnsFreeRecordList);
}
SetEvent(QueryContext->QueryCompletedEvent);
DeReferenceQueryContext(&QueryContext);
}