From d49d7b3282891359a11c7ed553e8a31632c5d039 Mon Sep 17 00:00:00 2001 From: Eric Kohl Date: Sun, 27 Oct 2019 13:45:52 +0100 Subject: [PATCH] [DNSRSLVR] Add the DNS Resolver Cache Service Patch written by Peter Hater and Christoph von Wittich. Slightly modified by me in order to - fix bit-rot - fix header include issues - disable integration with dnsapi because of confusing use of DnsQweryEx(). Integration with dnsapi will follow in a future commit. CORE-12159 --- base/services/CMakeLists.txt | 1 + base/services/dnsrslvr/CMakeLists.txt | 23 +++ base/services/dnsrslvr/cache.c | 199 ++++++++++++++++++++++++++ base/services/dnsrslvr/dnsrslvr.c | 150 +++++++++++++++++++ base/services/dnsrslvr/dnsrslvr.rc | 4 + base/services/dnsrslvr/dnsrslvr.spec | 1 + base/services/dnsrslvr/precomp.h | 45 ++++++ base/services/dnsrslvr/rpcserver.c | 130 +++++++++++++++++ media/inf/nettcpip.inf | 17 ++- sdk/include/reactos/idl/dnsrslvr.idl | 3 + sdk/include/reactos/windns_undoc.h | 6 + 11 files changed, 578 insertions(+), 1 deletion(-) create mode 100644 base/services/dnsrslvr/CMakeLists.txt create mode 100644 base/services/dnsrslvr/cache.c create mode 100644 base/services/dnsrslvr/dnsrslvr.c create mode 100644 base/services/dnsrslvr/dnsrslvr.rc create mode 100644 base/services/dnsrslvr/dnsrslvr.spec create mode 100644 base/services/dnsrslvr/precomp.h create mode 100644 base/services/dnsrslvr/rpcserver.c diff --git a/base/services/CMakeLists.txt b/base/services/CMakeLists.txt index 62b5a90ccd9..2605101c793 100644 --- a/base/services/CMakeLists.txt +++ b/base/services/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(audiosrv) add_subdirectory(dcomlaunch) add_subdirectory(dhcpcsvc) +add_subdirectory(dnsrslvr) add_subdirectory(eventlog) add_subdirectory(netlogon) add_subdirectory(nfsd) diff --git a/base/services/dnsrslvr/CMakeLists.txt b/base/services/dnsrslvr/CMakeLists.txt new file mode 100644 index 00000000000..a2821d93763 --- /dev/null +++ b/base/services/dnsrslvr/CMakeLists.txt @@ -0,0 +1,23 @@ + +include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl) +add_rpc_files(server ${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl/dnsrslvr.idl) + +list(APPEND SOURCE + cache.c + dnsrslvr.c + rpcserver.c + precomp.h + ${CMAKE_CURRENT_BINARY_DIR}/dnsrslvr_s.c) + +spec2def(dnsrslvr.dll dnsrslvr.spec ADD_IMPORTLIB) + +add_library(dnsrslvr SHARED ${SOURCE} dnsrslvr.rc ${CMAKE_CURRENT_BINARY_DIR}/dnsrslvr.def) + +if(NOT MSVC) + target_link_libraries(dnsrslvr ${PSEH_LIB}) +endif() + +set_module_type(dnsrslvr win32dll UNICODE) +add_importlibs(dnsrslvr advapi32 rpcrt4 dnsapi iphlpapi msvcrt kernel32 ntdll) +add_pch(dnsrslvr precomp.h SOURCE) +add_cd_file(TARGET dnsrslvr DESTINATION reactos/system32 FOR all) diff --git a/base/services/dnsrslvr/cache.c b/base/services/dnsrslvr/cache.c new file mode 100644 index 00000000000..6a6eee4844e --- /dev/null +++ b/base/services/dnsrslvr/cache.c @@ -0,0 +1,199 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: base/services/dnsrslvr/cache.c + * PURPOSE: DNS cache functions + * PROGRAMMER: Peter Hater + */ + +#include "precomp.h" + +//#define NDEBUG +#include + +static RESOLVER_CACHE DnsCache; +static BOOL DnsCacheInitialized = FALSE; + +#define DnsCacheLock() do { EnterCriticalSection(&DnsCache.Lock); } while (0) +#define DnsCacheUnlock() do { LeaveCriticalSection(&DnsCache.Lock); } while (0) + +VOID +DnsIntCacheInitialize(VOID) +{ + DPRINT("DnsIntCacheInitialize\n"); + + /* Check if we're initialized */ + if (DnsCacheInitialized) + return; + + /* Initialize the cache lock and namespace list */ + InitializeCriticalSection((LPCRITICAL_SECTION)&DnsCache.Lock); + InitializeListHead(&DnsCache.RecordList); + DnsCacheInitialized = TRUE; +} + +VOID +DnsIntCacheFree(VOID) +{ + DPRINT("DnsIntCacheFree\n"); + + /* Check if we're initialized */ + if (!DnsCacheInitialized) + return; + + if (!DnsCache.RecordList.Flink) + return; + + DnsIntCacheFlush(); + + DeleteCriticalSection(&DnsCache.Lock); + DnsCacheInitialized = FALSE; +} + +VOID +DnsIntCacheRemoveEntryItem(PRESOLVER_CACHE_ENTRY CacheEntry) +{ + DPRINT("DnsIntCacheRemoveEntryItem %p\n", CacheEntry); + + /* Remove the entry from the list */ + RemoveEntryList(&CacheEntry->CacheLink); + + /* Free record */ + DnsRecordListFree(CacheEntry->Record, DnsFreeRecordList); + + /* Delete us */ + HeapFree(GetProcessHeap(), 0, CacheEntry); +} + +VOID +DnsIntCacheFlush(VOID) +{ + PLIST_ENTRY Entry; + PRESOLVER_CACHE_ENTRY CacheEntry; + + DPRINT("DnsIntCacheFlush\n"); + + /* Lock the cache */ + DnsCacheLock(); + + /* Loop every entry */ + Entry = DnsCache.RecordList.Flink; + while (Entry != &DnsCache.RecordList) + { + /* Get this entry */ + CacheEntry = CONTAINING_RECORD(Entry, RESOLVER_CACHE_ENTRY, CacheLink); + + /* Remove it from list */ + DnsIntCacheRemoveEntryItem(CacheEntry); + + /* Move to the next entry */ + Entry = DnsCache.RecordList.Flink; + } + + /* Unlock the cache */ + DnsCacheUnlock(); +} + +BOOL +DnsIntCacheGetEntryFromName(LPCWSTR Name, + PDNS_RECORDW *Record) +{ + BOOL Ret = FALSE; + PRESOLVER_CACHE_ENTRY CacheEntry; + PLIST_ENTRY NextEntry; + + DPRINT("DnsIntCacheGetEntryFromName %ws %p\n", Name, Record); + + /* Assume failure */ + *Record = NULL; + + /* Lock the cache */ + DnsCacheLock(); + + /* Match the Id with all the entries in the List */ + NextEntry = DnsCache.RecordList.Flink; + while (NextEntry != &DnsCache.RecordList) + { + /* Get the Current Entry */ + CacheEntry = CONTAINING_RECORD(NextEntry, RESOLVER_CACHE_ENTRY, CacheLink); + + /* Check if this is the Catalog Entry ID we want */ + if (_wcsicmp(CacheEntry->Record->pName, Name) == 0) + { + /* Copy the entry and return it */ + *Record = DnsRecordSetCopyEx(CacheEntry->Record, DnsCharSetUnicode, DnsCharSetUnicode); + Ret = TRUE; + break; + } + + NextEntry = NextEntry->Flink; + } + + /* Release the cache */ + DnsCacheUnlock(); + + /* Return */ + return Ret; +} + +BOOL +DnsIntCacheRemoveEntryByName(LPCWSTR Name) +{ + BOOL Ret = FALSE; + PRESOLVER_CACHE_ENTRY CacheEntry; + PLIST_ENTRY NextEntry; + + DPRINT("DnsIntCacheRemoveEntryByName %ws\n", Name); + + /* Lock the cache */ + DnsCacheLock(); + + /* Match the Id with all the entries in the List */ + NextEntry = DnsCache.RecordList.Flink; + while (NextEntry != &DnsCache.RecordList) + { + /* Get the Current Entry */ + CacheEntry = CONTAINING_RECORD(NextEntry, RESOLVER_CACHE_ENTRY, CacheLink); + + /* Check if this is the Catalog Entry ID we want */ + if (_wcsicmp(CacheEntry->Record->pName, Name) == 0) + { + /* Remove the entry */ + DnsIntCacheRemoveEntryItem(CacheEntry); + Ret = TRUE; + break; + } + + NextEntry = NextEntry->Flink; + } + + /* Release the cache */ + DnsCacheUnlock(); + + /* Return */ + return Ret; +} + +VOID +DnsIntCacheAddEntry(PDNS_RECORDW Record) +{ + PRESOLVER_CACHE_ENTRY Entry; + + DPRINT("DnsIntCacheRemoveEntryByName %p\n", Record); + + /* Lock the cache */ + DnsCacheLock(); + + /* Match the Id with all the entries in the List */ + Entry = (PRESOLVER_CACHE_ENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(*Entry)); + if (!Entry) + return; + + Entry->Record = DnsRecordSetCopyEx(Record, DnsCharSetUnicode, DnsCharSetUnicode); + + /* Insert it to our List */ + InsertTailList(&DnsCache.RecordList, &Entry->CacheLink); + + /* Release the cache */ + DnsCacheUnlock(); +} diff --git a/base/services/dnsrslvr/dnsrslvr.c b/base/services/dnsrslvr/dnsrslvr.c new file mode 100644 index 00000000000..c0384050ca9 --- /dev/null +++ b/base/services/dnsrslvr/dnsrslvr.c @@ -0,0 +1,150 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS DNS Resolver + * FILE: base/services/dnsrslvr/dnsrslvr.c + * PURPOSE: DNS Resolver Service + * PROGRAMMER: Christoph von Wittich + */ + +/* INCLUDES *****************************************************************/ + +#include "precomp.h" + +#define NDEBUG +#include + +/* GLOBALS ******************************************************************/ + +HINSTANCE hDllInstance; +SERVICE_STATUS_HANDLE ServiceStatusHandle; +SERVICE_STATUS SvcStatus; +static WCHAR ServiceName[] = L"Dnscache"; + +DWORD WINAPI RpcThreadRoutine(LPVOID lpParameter); + +/* FUNCTIONS *****************************************************************/ + +static +VOID +UpdateServiceStatus( + HANDLE hServiceStatus, + DWORD NewStatus, + DWORD Increment) +{ + if (Increment > 0) + SvcStatus.dwCheckPoint += Increment; + else + SvcStatus.dwCheckPoint = 0; + + SvcStatus.dwCurrentState = NewStatus; + SetServiceStatus(hServiceStatus, &SvcStatus); +} + +static +DWORD +WINAPI +ServiceControlHandler( + DWORD dwControl, + DWORD dwEventType, + LPVOID lpEventData, + LPVOID lpContext) +{ + switch (dwControl) + { + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + UpdateServiceStatus(ServiceStatusHandle, SERVICE_STOP_PENDING, 1); + RpcMgmtStopServerListening(NULL); + DnsIntCacheFree(); + UpdateServiceStatus(ServiceStatusHandle, SERVICE_STOPPED, 0); + break; + + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } + return NO_ERROR; +} + +VOID +WINAPI +ServiceMain( + DWORD argc, + LPWSTR *argv) +{ + HANDLE hThread; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + DPRINT("ServiceMain() called\n"); + + SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + SvcStatus.dwCurrentState = SERVICE_START_PENDING; + SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + SvcStatus.dwCheckPoint = 0; + SvcStatus.dwWin32ExitCode = NO_ERROR; + SvcStatus.dwServiceSpecificExitCode = 0; + SvcStatus.dwWaitHint = 4000; + + ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName, + ServiceControlHandler, + NULL); + + if (!ServiceStatusHandle) + { + DPRINT1("DNSRSLVR: Unable to register service control handler (%lx)\n", GetLastError()); + return; // FALSE + } + + DnsIntCacheInitialize(); + + hThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE) + RpcThreadRoutine, + NULL, + 0, + NULL); + + if (!hThread) + { + DnsIntCacheFree(); + DPRINT("Can't create RpcThread\n"); + UpdateServiceStatus(ServiceStatusHandle, SERVICE_STOPPED, 0); + } + else + { + CloseHandle(hThread); + } + + DPRINT("ServiceMain() done\n"); + UpdateServiceStatus(ServiceStatusHandle, SERVICE_RUNNING, 0); +} + +BOOL +WINAPI +DllMain( + _In_ HINSTANCE hinstDLL, + _In_ DWORD fdwReason, + _In_ PVOID pvReserved) +{ + UNREFERENCED_PARAMETER(pvReserved); + + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + hDllInstance = hinstDLL; + break; + + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} + +/* EOF */ diff --git a/base/services/dnsrslvr/dnsrslvr.rc b/base/services/dnsrslvr/dnsrslvr.rc new file mode 100644 index 00000000000..482aefe129f --- /dev/null +++ b/base/services/dnsrslvr/dnsrslvr.rc @@ -0,0 +1,4 @@ +#define REACTOS_STR_FILE_DESCRIPTION "DNS-Client" +#define REACTOS_STR_INTERNAL_NAME "dnsrslvr" +#define REACTOS_STR_ORIGINAL_FILENAME "dnsrslvr.dll" +#include diff --git a/base/services/dnsrslvr/dnsrslvr.spec b/base/services/dnsrslvr/dnsrslvr.spec new file mode 100644 index 00000000000..1b27fe53864 --- /dev/null +++ b/base/services/dnsrslvr/dnsrslvr.spec @@ -0,0 +1 @@ +@ stdcall ServiceMain(long ptr) diff --git a/base/services/dnsrslvr/precomp.h b/base/services/dnsrslvr/precomp.h new file mode 100644 index 00000000000..0a11db5ee62 --- /dev/null +++ b/base/services/dnsrslvr/precomp.h @@ -0,0 +1,45 @@ +#ifndef _DNSRSLVR_PCH_ +#define _DNSRSLVR_PCH_ + +#include + +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#include +#include +#include +#include + +#include +#include + +#include + +typedef struct _RESOLVER_CACHE_ENTRY +{ + LIST_ENTRY CacheLink; + PDNS_RECORDW Record; +} RESOLVER_CACHE_ENTRY, *PRESOLVER_CACHE_ENTRY; + +typedef struct _RESOLVER_CACHE +{ + LIST_ENTRY RecordList; + CRITICAL_SECTION Lock; +} RESOLVER_CACHE, *PRESOLVER_CACHE; + + +/* cache.c */ + +VOID DnsIntCacheInitialize(VOID); +VOID DnsIntCacheRemoveEntryItem(PRESOLVER_CACHE_ENTRY CacheEntry); +VOID DnsIntCacheFree(VOID); +VOID DnsIntCacheFlush(VOID); +BOOL DnsIntCacheGetEntryFromName(LPCWSTR Name, + PDNS_RECORDW *Record); +VOID DnsIntCacheAddEntry(PDNS_RECORDW Record); +BOOL DnsIntCacheRemoveEntryByName(LPCWSTR Name); + + +#endif /* _DNSRSLVR_PCH_ */ diff --git a/base/services/dnsrslvr/rpcserver.c b/base/services/dnsrslvr/rpcserver.c new file mode 100644 index 00000000000..0cb5c1f8b59 --- /dev/null +++ b/base/services/dnsrslvr/rpcserver.c @@ -0,0 +1,130 @@ +/* + * PROJECT: ReactOS DNS Resolver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: base/services/dnsrslvr/rpcserver.c + * PURPOSE: RPC server interface + * COPYRIGHT: Copyright 2016 Christoph von Wittich + */ + +#include "precomp.h" + +#define NDEBUG +#include + +DWORD +WINAPI +RpcThreadRoutine(LPVOID lpParameter) +{ + RPC_STATUS Status; + + Status = RpcServerUseProtseqEpW(L"ncalrpc", 20, L"DNSResolver", NULL); + if (Status != RPC_S_OK) + { + DPRINT("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status); + return 0; + } + + Status = RpcServerRegisterIf(DnsResolver_v2_0_s_ifspec, NULL, NULL); + if (Status != RPC_S_OK) + { + DPRINT("RpcServerRegisterIf() failed (Status %lx)\n", Status); + return 0; + } + + Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0); + if (Status != RPC_S_OK) + { + DPRINT("RpcServerListen() failed (Status %lx)\n", Status); + } + + DPRINT("RpcServerListen finished\n"); + return 0; +} + +DWORD +R_ResolverFlushCache( + DNSRSLVR_HANDLE pwszServerName) +{ + // FIXME Should store (and flush) entries by server handle + DnsIntCacheFlush(); + return 0; +} + +DWORD +R_ResolverQuery( + DNSRSLVR_HANDLE pwszServerName, + LPCWSTR pwsName, + WORD wType, + DWORD Flags, + DWORD *dwRecords, + DNS_RECORDW **ppResultRecords) +{ +#if 0 + DNS_QUERY_REQUEST QueryRequest = { 0 }; + DNS_QUERY_RESULT QueryResults = { 0 }; +#endif + DNS_STATUS Status; + PDNS_RECORDW Record; + + DPRINT1("R_ResolverQuery %p %p %x %lx %p %p\n", + pwszServerName, pwsName, wType, Flags, dwRecords, ppResultRecords); + + if (!pwszServerName || !pwsName || !wType || !ppResultRecords) + return ERROR_INVALID_PARAMETER; + + // FIXME Should lookup entries by server handle + if (DnsIntCacheGetEntryFromName(pwsName, ppResultRecords)) + { + Status = ERROR_SUCCESS; + } + else + { +#if 0 + QueryRequest.Version = DNS_QUERY_REQUEST_VERSION1; + QueryRequest.QueryType = wType; + QueryRequest.QueryName = pwsName; + QueryRequest.QueryOptions = Flags; + QueryResults.Version = DNS_QUERY_REQUEST_VERSION1; + + Status = DnsQueryEx(&QueryRequest, &QueryResults, NULL); + if (Status == ERROR_SUCCESS) + { + // FIXME Should store (and flush) entries by server handle + DnsIntCacheAddEntry(QueryResults.pQueryRecords); + *ppResultRecords = QueryResults.pQueryRecords; + } +#endif + } + + if (dwRecords) + *dwRecords = 0; + + if (Status == ERROR_SUCCESS) + { + Record = *ppResultRecords; + while (Record) + { + if (dwRecords) + (*dwRecords)++; + Record = Record->pNext; + } + } + + DPRINT1("R_ResolverQuery result %ld %ld\n", Status, *dwRecords); + + return Status; +} + +void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); +} + +void __RPC_USER midl_user_free(void __RPC_FAR * ptr) +{ + HeapFree(GetProcessHeap(), 0, ptr); +} + +void __RPC_USER WLANSVC_RPC_HANDLE_rundown(DNSRSLVR_HANDLE hClientHandle) +{ +} diff --git a/media/inf/nettcpip.inf b/media/inf/nettcpip.inf index eaf1b7cc9eb..d179e9d0499 100644 --- a/media/inf/nettcpip.inf +++ b/media/inf/nettcpip.inf @@ -27,7 +27,7 @@ AddReg = TCPIP_AddReg_Global.NT HKR,"Ndi","ClsId",0x00000000,"{A907657F-6FDF-11D0-8EFB-00C04FD912B2}" HKR,"Ndi","HelpText",0x00000000,"Transmission Control Protocol/Internet Protocol" HKR,"Ndi","Service",0x00000000,"Tcpip" -HKR,"Ndi","CoServices",0x00010000,"Tcpip","Dhcp" +HKR,"Ndi","CoServices",0x00010000,"Tcpip","Dhcp","Dnscache" ; TCP/IPv4 driver ; NOTE: These settings should be added by the network setup @@ -236,6 +236,7 @@ HKLM,"SYSTEM\CurrentControlSet\Services\Winsock2\Parameters\Protocol_Catalog9\Ca [MS_TCPIP.PrimaryInstall.Services] AddService = Tcpip, , tcpip_Service_Inst AddService = DHCP, , dhcp_Service_Inst +AddService = Dnscache, , dns_Service_Inst [tcpip_Service_Inst] ServiceType = 1 @@ -275,6 +276,20 @@ AddReg=dhcp_AddReg HKR,,"ObjectName",0x00000000,"LocalSystem" HKR,"Parameters","ServiceDll",0x00020000,"%SystemRoot%\system32\dhcpcsvc.dll" +[dns_Service_Inst] +DisplayName = "DNS Client" +Description = "Service that caches local DNS queries" +ServiceType = 0x20 +StartType = 2 +ErrorControl = 1 +ServiceBinary = "%11%\svchost.exe -k netsvcs" +LoadOrderGroup = TDI +AddReg=dns_AddReg + +[dns_AddReg] +HKR,,"ObjectName",0x00000000,"LocalSystem" +HKR,"Parameters","ServiceDll",0x00020000,"%SystemRoot%\system32\dnsrslvr.dll" + ;-------------------------------- STRINGS ------------------------------- [Strings] diff --git a/sdk/include/reactos/idl/dnsrslvr.idl b/sdk/include/reactos/idl/dnsrslvr.idl index ee5af553fb5..787e5c71267 100644 --- a/sdk/include/reactos/idl/dnsrslvr.idl +++ b/sdk/include/reactos/idl/dnsrslvr.idl @@ -6,7 +6,10 @@ #define UNICODE #include + +cpp_quote("#ifndef _WINDNS_INCLUDED_") #include +cpp_quote("#endif") typedef [handle, string] LPWSTR DNSRSLVR_HANDLE; diff --git a/sdk/include/reactos/windns_undoc.h b/sdk/include/reactos/windns_undoc.h index 3af80a1dfc5..01e6f412095 100644 --- a/sdk/include/reactos/windns_undoc.h +++ b/sdk/include/reactos/windns_undoc.h @@ -14,6 +14,10 @@ typedef struct _DNS_CACHE_ENTRY unsigned short wFlags; /* DNS Record Flags */ } DNS_CACHE_ENTRY, *PDNS_CACHE_ENTRY; + +#ifndef __WIDL__ +// Hack + BOOL WINAPI DnsFlushResolverCache(VOID); @@ -23,6 +27,8 @@ WINAPI DnsGetCacheDataTable( _Out_ PDNS_CACHE_ENTRY *DnsCache); +#endif /* __WIDL__ */ + #ifdef __cplusplus } #endif