Reactos

[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

+578 -1
+1
base/services/CMakeLists.txt
··· 2 2 add_subdirectory(audiosrv) 3 3 add_subdirectory(dcomlaunch) 4 4 add_subdirectory(dhcpcsvc) 5 + add_subdirectory(dnsrslvr) 5 6 add_subdirectory(eventlog) 6 7 add_subdirectory(netlogon) 7 8 add_subdirectory(nfsd)
+23
base/services/dnsrslvr/CMakeLists.txt
··· 1 + 2 + include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl) 3 + add_rpc_files(server ${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl/dnsrslvr.idl) 4 + 5 + list(APPEND SOURCE 6 + cache.c 7 + dnsrslvr.c 8 + rpcserver.c 9 + precomp.h 10 + ${CMAKE_CURRENT_BINARY_DIR}/dnsrslvr_s.c) 11 + 12 + spec2def(dnsrslvr.dll dnsrslvr.spec ADD_IMPORTLIB) 13 + 14 + add_library(dnsrslvr SHARED ${SOURCE} dnsrslvr.rc ${CMAKE_CURRENT_BINARY_DIR}/dnsrslvr.def) 15 + 16 + if(NOT MSVC) 17 + target_link_libraries(dnsrslvr ${PSEH_LIB}) 18 + endif() 19 + 20 + set_module_type(dnsrslvr win32dll UNICODE) 21 + add_importlibs(dnsrslvr advapi32 rpcrt4 dnsapi iphlpapi msvcrt kernel32 ntdll) 22 + add_pch(dnsrslvr precomp.h SOURCE) 23 + add_cd_file(TARGET dnsrslvr DESTINATION reactos/system32 FOR all)
+199
base/services/dnsrslvr/cache.c
··· 1 + /* 2 + * COPYRIGHT: See COPYING in the top level directory 3 + * PROJECT: ReactOS system libraries 4 + * FILE: base/services/dnsrslvr/cache.c 5 + * PURPOSE: DNS cache functions 6 + * PROGRAMMER: Peter Hater 7 + */ 8 + 9 + #include "precomp.h" 10 + 11 + //#define NDEBUG 12 + #include <debug.h> 13 + 14 + static RESOLVER_CACHE DnsCache; 15 + static BOOL DnsCacheInitialized = FALSE; 16 + 17 + #define DnsCacheLock() do { EnterCriticalSection(&DnsCache.Lock); } while (0) 18 + #define DnsCacheUnlock() do { LeaveCriticalSection(&DnsCache.Lock); } while (0) 19 + 20 + VOID 21 + DnsIntCacheInitialize(VOID) 22 + { 23 + DPRINT("DnsIntCacheInitialize\n"); 24 + 25 + /* Check if we're initialized */ 26 + if (DnsCacheInitialized) 27 + return; 28 + 29 + /* Initialize the cache lock and namespace list */ 30 + InitializeCriticalSection((LPCRITICAL_SECTION)&DnsCache.Lock); 31 + InitializeListHead(&DnsCache.RecordList); 32 + DnsCacheInitialized = TRUE; 33 + } 34 + 35 + VOID 36 + DnsIntCacheFree(VOID) 37 + { 38 + DPRINT("DnsIntCacheFree\n"); 39 + 40 + /* Check if we're initialized */ 41 + if (!DnsCacheInitialized) 42 + return; 43 + 44 + if (!DnsCache.RecordList.Flink) 45 + return; 46 + 47 + DnsIntCacheFlush(); 48 + 49 + DeleteCriticalSection(&DnsCache.Lock); 50 + DnsCacheInitialized = FALSE; 51 + } 52 + 53 + VOID 54 + DnsIntCacheRemoveEntryItem(PRESOLVER_CACHE_ENTRY CacheEntry) 55 + { 56 + DPRINT("DnsIntCacheRemoveEntryItem %p\n", CacheEntry); 57 + 58 + /* Remove the entry from the list */ 59 + RemoveEntryList(&CacheEntry->CacheLink); 60 + 61 + /* Free record */ 62 + DnsRecordListFree(CacheEntry->Record, DnsFreeRecordList); 63 + 64 + /* Delete us */ 65 + HeapFree(GetProcessHeap(), 0, CacheEntry); 66 + } 67 + 68 + VOID 69 + DnsIntCacheFlush(VOID) 70 + { 71 + PLIST_ENTRY Entry; 72 + PRESOLVER_CACHE_ENTRY CacheEntry; 73 + 74 + DPRINT("DnsIntCacheFlush\n"); 75 + 76 + /* Lock the cache */ 77 + DnsCacheLock(); 78 + 79 + /* Loop every entry */ 80 + Entry = DnsCache.RecordList.Flink; 81 + while (Entry != &DnsCache.RecordList) 82 + { 83 + /* Get this entry */ 84 + CacheEntry = CONTAINING_RECORD(Entry, RESOLVER_CACHE_ENTRY, CacheLink); 85 + 86 + /* Remove it from list */ 87 + DnsIntCacheRemoveEntryItem(CacheEntry); 88 + 89 + /* Move to the next entry */ 90 + Entry = DnsCache.RecordList.Flink; 91 + } 92 + 93 + /* Unlock the cache */ 94 + DnsCacheUnlock(); 95 + } 96 + 97 + BOOL 98 + DnsIntCacheGetEntryFromName(LPCWSTR Name, 99 + PDNS_RECORDW *Record) 100 + { 101 + BOOL Ret = FALSE; 102 + PRESOLVER_CACHE_ENTRY CacheEntry; 103 + PLIST_ENTRY NextEntry; 104 + 105 + DPRINT("DnsIntCacheGetEntryFromName %ws %p\n", Name, Record); 106 + 107 + /* Assume failure */ 108 + *Record = NULL; 109 + 110 + /* Lock the cache */ 111 + DnsCacheLock(); 112 + 113 + /* Match the Id with all the entries in the List */ 114 + NextEntry = DnsCache.RecordList.Flink; 115 + while (NextEntry != &DnsCache.RecordList) 116 + { 117 + /* Get the Current Entry */ 118 + CacheEntry = CONTAINING_RECORD(NextEntry, RESOLVER_CACHE_ENTRY, CacheLink); 119 + 120 + /* Check if this is the Catalog Entry ID we want */ 121 + if (_wcsicmp(CacheEntry->Record->pName, Name) == 0) 122 + { 123 + /* Copy the entry and return it */ 124 + *Record = DnsRecordSetCopyEx(CacheEntry->Record, DnsCharSetUnicode, DnsCharSetUnicode); 125 + Ret = TRUE; 126 + break; 127 + } 128 + 129 + NextEntry = NextEntry->Flink; 130 + } 131 + 132 + /* Release the cache */ 133 + DnsCacheUnlock(); 134 + 135 + /* Return */ 136 + return Ret; 137 + } 138 + 139 + BOOL 140 + DnsIntCacheRemoveEntryByName(LPCWSTR Name) 141 + { 142 + BOOL Ret = FALSE; 143 + PRESOLVER_CACHE_ENTRY CacheEntry; 144 + PLIST_ENTRY NextEntry; 145 + 146 + DPRINT("DnsIntCacheRemoveEntryByName %ws\n", Name); 147 + 148 + /* Lock the cache */ 149 + DnsCacheLock(); 150 + 151 + /* Match the Id with all the entries in the List */ 152 + NextEntry = DnsCache.RecordList.Flink; 153 + while (NextEntry != &DnsCache.RecordList) 154 + { 155 + /* Get the Current Entry */ 156 + CacheEntry = CONTAINING_RECORD(NextEntry, RESOLVER_CACHE_ENTRY, CacheLink); 157 + 158 + /* Check if this is the Catalog Entry ID we want */ 159 + if (_wcsicmp(CacheEntry->Record->pName, Name) == 0) 160 + { 161 + /* Remove the entry */ 162 + DnsIntCacheRemoveEntryItem(CacheEntry); 163 + Ret = TRUE; 164 + break; 165 + } 166 + 167 + NextEntry = NextEntry->Flink; 168 + } 169 + 170 + /* Release the cache */ 171 + DnsCacheUnlock(); 172 + 173 + /* Return */ 174 + return Ret; 175 + } 176 + 177 + VOID 178 + DnsIntCacheAddEntry(PDNS_RECORDW Record) 179 + { 180 + PRESOLVER_CACHE_ENTRY Entry; 181 + 182 + DPRINT("DnsIntCacheRemoveEntryByName %p\n", Record); 183 + 184 + /* Lock the cache */ 185 + DnsCacheLock(); 186 + 187 + /* Match the Id with all the entries in the List */ 188 + Entry = (PRESOLVER_CACHE_ENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(*Entry)); 189 + if (!Entry) 190 + return; 191 + 192 + Entry->Record = DnsRecordSetCopyEx(Record, DnsCharSetUnicode, DnsCharSetUnicode); 193 + 194 + /* Insert it to our List */ 195 + InsertTailList(&DnsCache.RecordList, &Entry->CacheLink); 196 + 197 + /* Release the cache */ 198 + DnsCacheUnlock(); 199 + }
+150
base/services/dnsrslvr/dnsrslvr.c
··· 1 + /* 2 + * COPYRIGHT: See COPYING in the top level directory 3 + * PROJECT: ReactOS DNS Resolver 4 + * FILE: base/services/dnsrslvr/dnsrslvr.c 5 + * PURPOSE: DNS Resolver Service 6 + * PROGRAMMER: Christoph von Wittich 7 + */ 8 + 9 + /* INCLUDES *****************************************************************/ 10 + 11 + #include "precomp.h" 12 + 13 + #define NDEBUG 14 + #include <debug.h> 15 + 16 + /* GLOBALS ******************************************************************/ 17 + 18 + HINSTANCE hDllInstance; 19 + SERVICE_STATUS_HANDLE ServiceStatusHandle; 20 + SERVICE_STATUS SvcStatus; 21 + static WCHAR ServiceName[] = L"Dnscache"; 22 + 23 + DWORD WINAPI RpcThreadRoutine(LPVOID lpParameter); 24 + 25 + /* FUNCTIONS *****************************************************************/ 26 + 27 + static 28 + VOID 29 + UpdateServiceStatus( 30 + HANDLE hServiceStatus, 31 + DWORD NewStatus, 32 + DWORD Increment) 33 + { 34 + if (Increment > 0) 35 + SvcStatus.dwCheckPoint += Increment; 36 + else 37 + SvcStatus.dwCheckPoint = 0; 38 + 39 + SvcStatus.dwCurrentState = NewStatus; 40 + SetServiceStatus(hServiceStatus, &SvcStatus); 41 + } 42 + 43 + static 44 + DWORD 45 + WINAPI 46 + ServiceControlHandler( 47 + DWORD dwControl, 48 + DWORD dwEventType, 49 + LPVOID lpEventData, 50 + LPVOID lpContext) 51 + { 52 + switch (dwControl) 53 + { 54 + case SERVICE_CONTROL_SHUTDOWN: 55 + case SERVICE_CONTROL_STOP: 56 + UpdateServiceStatus(ServiceStatusHandle, SERVICE_STOP_PENDING, 1); 57 + RpcMgmtStopServerListening(NULL); 58 + DnsIntCacheFree(); 59 + UpdateServiceStatus(ServiceStatusHandle, SERVICE_STOPPED, 0); 60 + break; 61 + 62 + case SERVICE_CONTROL_INTERROGATE: 63 + return NO_ERROR; 64 + 65 + default: 66 + return ERROR_CALL_NOT_IMPLEMENTED; 67 + } 68 + return NO_ERROR; 69 + } 70 + 71 + VOID 72 + WINAPI 73 + ServiceMain( 74 + DWORD argc, 75 + LPWSTR *argv) 76 + { 77 + HANDLE hThread; 78 + 79 + UNREFERENCED_PARAMETER(argc); 80 + UNREFERENCED_PARAMETER(argv); 81 + 82 + DPRINT("ServiceMain() called\n"); 83 + 84 + SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 85 + SvcStatus.dwCurrentState = SERVICE_START_PENDING; 86 + SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 87 + SvcStatus.dwCheckPoint = 0; 88 + SvcStatus.dwWin32ExitCode = NO_ERROR; 89 + SvcStatus.dwServiceSpecificExitCode = 0; 90 + SvcStatus.dwWaitHint = 4000; 91 + 92 + ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName, 93 + ServiceControlHandler, 94 + NULL); 95 + 96 + if (!ServiceStatusHandle) 97 + { 98 + DPRINT1("DNSRSLVR: Unable to register service control handler (%lx)\n", GetLastError()); 99 + return; // FALSE 100 + } 101 + 102 + DnsIntCacheInitialize(); 103 + 104 + hThread = CreateThread(NULL, 105 + 0, 106 + (LPTHREAD_START_ROUTINE) 107 + RpcThreadRoutine, 108 + NULL, 109 + 0, 110 + NULL); 111 + 112 + if (!hThread) 113 + { 114 + DnsIntCacheFree(); 115 + DPRINT("Can't create RpcThread\n"); 116 + UpdateServiceStatus(ServiceStatusHandle, SERVICE_STOPPED, 0); 117 + } 118 + else 119 + { 120 + CloseHandle(hThread); 121 + } 122 + 123 + DPRINT("ServiceMain() done\n"); 124 + UpdateServiceStatus(ServiceStatusHandle, SERVICE_RUNNING, 0); 125 + } 126 + 127 + BOOL 128 + WINAPI 129 + DllMain( 130 + _In_ HINSTANCE hinstDLL, 131 + _In_ DWORD fdwReason, 132 + _In_ PVOID pvReserved) 133 + { 134 + UNREFERENCED_PARAMETER(pvReserved); 135 + 136 + switch (fdwReason) 137 + { 138 + case DLL_PROCESS_ATTACH: 139 + DisableThreadLibraryCalls(hinstDLL); 140 + hDllInstance = hinstDLL; 141 + break; 142 + 143 + case DLL_PROCESS_DETACH: 144 + break; 145 + } 146 + 147 + return TRUE; 148 + } 149 + 150 + /* EOF */
+4
base/services/dnsrslvr/dnsrslvr.rc
··· 1 + #define REACTOS_STR_FILE_DESCRIPTION "DNS-Client" 2 + #define REACTOS_STR_INTERNAL_NAME "dnsrslvr" 3 + #define REACTOS_STR_ORIGINAL_FILENAME "dnsrslvr.dll" 4 + #include <reactos/version.rc>
+1
base/services/dnsrslvr/dnsrslvr.spec
··· 1 + @ stdcall ServiceMain(long ptr)
+45
base/services/dnsrslvr/precomp.h
··· 1 + #ifndef _DNSRSLVR_PCH_ 2 + #define _DNSRSLVR_PCH_ 3 + 4 + #include <stdarg.h> 5 + 6 + #define WIN32_NO_STATUS 7 + #define _INC_WINDOWS 8 + #define COM_NO_WINDOWS_H 9 + 10 + #include <windef.h> 11 + #include <winbase.h> 12 + #include <winsvc.h> 13 + #include <windns.h> 14 + 15 + #include <ndk/rtlfuncs.h> 16 + #include <ndk/obfuncs.h> 17 + 18 + #include <dnsrslvr_s.h> 19 + 20 + typedef struct _RESOLVER_CACHE_ENTRY 21 + { 22 + LIST_ENTRY CacheLink; 23 + PDNS_RECORDW Record; 24 + } RESOLVER_CACHE_ENTRY, *PRESOLVER_CACHE_ENTRY; 25 + 26 + typedef struct _RESOLVER_CACHE 27 + { 28 + LIST_ENTRY RecordList; 29 + CRITICAL_SECTION Lock; 30 + } RESOLVER_CACHE, *PRESOLVER_CACHE; 31 + 32 + 33 + /* cache.c */ 34 + 35 + VOID DnsIntCacheInitialize(VOID); 36 + VOID DnsIntCacheRemoveEntryItem(PRESOLVER_CACHE_ENTRY CacheEntry); 37 + VOID DnsIntCacheFree(VOID); 38 + VOID DnsIntCacheFlush(VOID); 39 + BOOL DnsIntCacheGetEntryFromName(LPCWSTR Name, 40 + PDNS_RECORDW *Record); 41 + VOID DnsIntCacheAddEntry(PDNS_RECORDW Record); 42 + BOOL DnsIntCacheRemoveEntryByName(LPCWSTR Name); 43 + 44 + 45 + #endif /* _DNSRSLVR_PCH_ */
+130
base/services/dnsrslvr/rpcserver.c
··· 1 + /* 2 + * PROJECT: ReactOS DNS Resolver 3 + * LICENSE: GPL - See COPYING in the top level directory 4 + * FILE: base/services/dnsrslvr/rpcserver.c 5 + * PURPOSE: RPC server interface 6 + * COPYRIGHT: Copyright 2016 Christoph von Wittich 7 + */ 8 + 9 + #include "precomp.h" 10 + 11 + #define NDEBUG 12 + #include <debug.h> 13 + 14 + DWORD 15 + WINAPI 16 + RpcThreadRoutine(LPVOID lpParameter) 17 + { 18 + RPC_STATUS Status; 19 + 20 + Status = RpcServerUseProtseqEpW(L"ncalrpc", 20, L"DNSResolver", NULL); 21 + if (Status != RPC_S_OK) 22 + { 23 + DPRINT("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status); 24 + return 0; 25 + } 26 + 27 + Status = RpcServerRegisterIf(DnsResolver_v2_0_s_ifspec, NULL, NULL); 28 + if (Status != RPC_S_OK) 29 + { 30 + DPRINT("RpcServerRegisterIf() failed (Status %lx)\n", Status); 31 + return 0; 32 + } 33 + 34 + Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0); 35 + if (Status != RPC_S_OK) 36 + { 37 + DPRINT("RpcServerListen() failed (Status %lx)\n", Status); 38 + } 39 + 40 + DPRINT("RpcServerListen finished\n"); 41 + return 0; 42 + } 43 + 44 + DWORD 45 + R_ResolverFlushCache( 46 + DNSRSLVR_HANDLE pwszServerName) 47 + { 48 + // FIXME Should store (and flush) entries by server handle 49 + DnsIntCacheFlush(); 50 + return 0; 51 + } 52 + 53 + DWORD 54 + R_ResolverQuery( 55 + DNSRSLVR_HANDLE pwszServerName, 56 + LPCWSTR pwsName, 57 + WORD wType, 58 + DWORD Flags, 59 + DWORD *dwRecords, 60 + DNS_RECORDW **ppResultRecords) 61 + { 62 + #if 0 63 + DNS_QUERY_REQUEST QueryRequest = { 0 }; 64 + DNS_QUERY_RESULT QueryResults = { 0 }; 65 + #endif 66 + DNS_STATUS Status; 67 + PDNS_RECORDW Record; 68 + 69 + DPRINT1("R_ResolverQuery %p %p %x %lx %p %p\n", 70 + pwszServerName, pwsName, wType, Flags, dwRecords, ppResultRecords); 71 + 72 + if (!pwszServerName || !pwsName || !wType || !ppResultRecords) 73 + return ERROR_INVALID_PARAMETER; 74 + 75 + // FIXME Should lookup entries by server handle 76 + if (DnsIntCacheGetEntryFromName(pwsName, ppResultRecords)) 77 + { 78 + Status = ERROR_SUCCESS; 79 + } 80 + else 81 + { 82 + #if 0 83 + QueryRequest.Version = DNS_QUERY_REQUEST_VERSION1; 84 + QueryRequest.QueryType = wType; 85 + QueryRequest.QueryName = pwsName; 86 + QueryRequest.QueryOptions = Flags; 87 + QueryResults.Version = DNS_QUERY_REQUEST_VERSION1; 88 + 89 + Status = DnsQueryEx(&QueryRequest, &QueryResults, NULL); 90 + if (Status == ERROR_SUCCESS) 91 + { 92 + // FIXME Should store (and flush) entries by server handle 93 + DnsIntCacheAddEntry(QueryResults.pQueryRecords); 94 + *ppResultRecords = QueryResults.pQueryRecords; 95 + } 96 + #endif 97 + } 98 + 99 + if (dwRecords) 100 + *dwRecords = 0; 101 + 102 + if (Status == ERROR_SUCCESS) 103 + { 104 + Record = *ppResultRecords; 105 + while (Record) 106 + { 107 + if (dwRecords) 108 + (*dwRecords)++; 109 + Record = Record->pNext; 110 + } 111 + } 112 + 113 + DPRINT1("R_ResolverQuery result %ld %ld\n", Status, *dwRecords); 114 + 115 + return Status; 116 + } 117 + 118 + void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len) 119 + { 120 + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); 121 + } 122 + 123 + void __RPC_USER midl_user_free(void __RPC_FAR * ptr) 124 + { 125 + HeapFree(GetProcessHeap(), 0, ptr); 126 + } 127 + 128 + void __RPC_USER WLANSVC_RPC_HANDLE_rundown(DNSRSLVR_HANDLE hClientHandle) 129 + { 130 + }
+16 -1
media/inf/nettcpip.inf
··· 27 27 HKR,"Ndi","ClsId",0x00000000,"{A907657F-6FDF-11D0-8EFB-00C04FD912B2}" 28 28 HKR,"Ndi","HelpText",0x00000000,"Transmission Control Protocol/Internet Protocol" 29 29 HKR,"Ndi","Service",0x00000000,"Tcpip" 30 - HKR,"Ndi","CoServices",0x00010000,"Tcpip","Dhcp" 30 + HKR,"Ndi","CoServices",0x00010000,"Tcpip","Dhcp","Dnscache" 31 31 32 32 ; TCP/IPv4 driver 33 33 ; NOTE: These settings should be added by the network setup ··· 236 236 [MS_TCPIP.PrimaryInstall.Services] 237 237 AddService = Tcpip, , tcpip_Service_Inst 238 238 AddService = DHCP, , dhcp_Service_Inst 239 + AddService = Dnscache, , dns_Service_Inst 239 240 240 241 [tcpip_Service_Inst] 241 242 ServiceType = 1 ··· 274 275 [dhcp_AddReg] 275 276 HKR,,"ObjectName",0x00000000,"LocalSystem" 276 277 HKR,"Parameters","ServiceDll",0x00020000,"%SystemRoot%\system32\dhcpcsvc.dll" 278 + 279 + [dns_Service_Inst] 280 + DisplayName = "DNS Client" 281 + Description = "Service that caches local DNS queries" 282 + ServiceType = 0x20 283 + StartType = 2 284 + ErrorControl = 1 285 + ServiceBinary = "%11%\svchost.exe -k netsvcs" 286 + LoadOrderGroup = TDI 287 + AddReg=dns_AddReg 288 + 289 + [dns_AddReg] 290 + HKR,,"ObjectName",0x00000000,"LocalSystem" 291 + HKR,"Parameters","ServiceDll",0x00020000,"%SystemRoot%\system32\dnsrslvr.dll" 277 292 278 293 ;-------------------------------- STRINGS ------------------------------- 279 294
+3
sdk/include/reactos/idl/dnsrslvr.idl
··· 6 6 7 7 #define UNICODE 8 8 #include <sal.h> 9 + 10 + cpp_quote("#ifndef _WINDNS_INCLUDED_") 9 11 #include <windns.h> 12 + cpp_quote("#endif") 10 13 11 14 typedef [handle, string] LPWSTR DNSRSLVR_HANDLE; 12 15
+6
sdk/include/reactos/windns_undoc.h
··· 14 14 unsigned short wFlags; /* DNS Record Flags */ 15 15 } DNS_CACHE_ENTRY, *PDNS_CACHE_ENTRY; 16 16 17 + 18 + #ifndef __WIDL__ 19 + // Hack 20 + 17 21 BOOL 18 22 WINAPI 19 23 DnsFlushResolverCache(VOID); ··· 22 26 WINAPI 23 27 DnsGetCacheDataTable( 24 28 _Out_ PDNS_CACHE_ENTRY *DnsCache); 29 + 30 + #endif /* __WIDL__ */ 25 31 26 32 #ifdef __cplusplus 27 33 }