/*++ Copyright (c) 2000, Microsoft Corporation Module Name: dnsfile.c Abstract: This module contains code for the Simple DNS Server (formerly the DNS Proxy) to operate on the Hosts.ics file. Abridged from the DS tree. Author: Raghu Gatta (rgatta) 15-Nov-2000 Revision History: --*/ #include "precomp.h" #pragma hdrstop // // EXTERNAL DECLARATIONS // extern "C" FILE * SockOpenNetworkDataBase( IN char *Database, OUT char *Pathname, IN int PathnameLen, IN char *OpenFlags ); // // Locking Order: // (1) DnsFileInfo.Lock // (2) DnsTableLock // OR // (1) DnsGlobalInfoLock // (2) DnsTableLock // // Globals // IP_DNS_PROXY_FILE_INFO DnsFileInfo; ULONG DnsInitializeFileManagement( VOID ) /*++ Routine Description: This routine is called to initialize the file management module. Arguments: none. Return Value: ULONG - Win32 status code. Environment: Invoked internally in the context of an IP router-manager thread. (See 'RMDNS.C'). --*/ { ULONG Error = NO_ERROR; PROFILE("DnsInitializeFileManagement"); ZeroMemory(&DnsFileInfo, sizeof(IP_DNS_PROXY_FILE_INFO)); __try { InitializeCriticalSection(&DnsFileInfo.Lock); } __except(EXCEPTION_EXECUTE_HANDLER) { NhTrace( TRACE_FLAG_IF, "DnsInitializeFileManagement: exception %d creating lock", Error = GetExceptionCode() ); } return Error; } // DnsInitializeFileManagement VOID DnsShutdownFileManagement( VOID ) /*++ Routine Description: This routine is called to shutdown the file management module. Arguments: none. Return Value: none. Environment: Invoked in an arbitrary thread context. --*/ { PROFILE("DnsShutdownFileManagement"); DnsEndHostsIcsFile(); DeleteCriticalSection(&DnsFileInfo.Lock); } // DnsShutdownFileManagement BOOL DnsEndHostsIcsFile( VOID ) /*++ Routine Description: Close hosts file. Arguments: None. Globals: DnsFileInfo.HostFile -- host file ptr, tested and cleared Return Value: None. --*/ { if (DnsFileInfo.HostFile) { fclose(DnsFileInfo.HostFile); DnsFileInfo.HostFile = NULL; } return TRUE; } // DnsEndHostsIcsFile BOOL DnsSetHostsIcsFile( BOOL fOverwrite ) /*++ Routine Description: Open hosts.ics file. If we write, we overwrite, else we read Arguments: None. Globals: DnsFileInfo.HostFile -- host file ptr, tested and set Return Value: None. --*/ { LPVOID lpMsgBuf; UINT len; WCHAR hostDirectory[MAX_PATH*2]; PCHAR mode = fOverwrite ? "w+t" : "rt"; DnsEndHostsIcsFile(); // // reset the host file name to hosts.ics // ZeroMemory(DnsFileInfo.HostFileName, HOSTDB_SIZE); DnsFileInfo.HostFile = SockOpenNetworkDataBase( HOSTSICSFILE, DnsFileInfo.HostFileName, HOSTDB_SIZE, mode ); if(DnsFileInfo.HostFile == NULL) { NhTrace( TRACE_FLAG_DNS, "DnsSetHostsIcsFile: Unable to open %s file", HOSTSICSFILE ); return FALSE; } else { /* NhTrace( TRACE_FLAG_DNS, "DnsSetHostsIcsFile: Successfully opened %s file", DnsFileInfo.HostFileName ); */ } return TRUE; } // DnsSetHostsIcsFile BOOL GetHostFromHostsIcsFile( BOOL fStartup ) /*++ Routine Description: Reads an entry from hosts.ics file. Arguments: fStartup set to TRUE if called on startup of protocol Globals: DnsFileInfo.HostFile -- host file ptr, tested and set DnsFileInfo.HostTime -- host timestamp DnsFileInfo.pHostName -- name ptr is set DnsFileInfo.Ip4Address -- IP4 address is set Return Value: TRUE if we were able to successfully able to read a line. FALSE if on EOF or no hosts file found. --*/ { char *p, *ep; register char *cp, **q; // // we assume the hosts.ics file has already been opened // if (DnsFileInfo.HostFile == NULL) { return FALSE; } DnsFileInfo.HostLineBuf[BUFSIZ] = NULL; DnsFileInfo.pHostName = NULL; DnsFileInfo.Ip4Address = 0; ZeroMemory(&DnsFileInfo.HostTime, sizeof(SYSTEMTIME)); // // loop until successfully read IP address // IP address starts on column 1 // while( 1 ) { // quit on EOF if ((p = fgets(DnsFileInfo.HostLineBuf, BUFSIZ, DnsFileInfo.HostFile)) == NULL) { if (!feof(DnsFileInfo.HostFile)) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error reading line" ); } return FALSE; } // comment line -- skip if (*p == '#') { p++; // // if in startup mode, we skip first comment sign; // if there are more comment signs -- skip // if ((fStartup && *p == '#') || !fStartup) { continue; } } // null address terminate at EOL cp = strpbrk(p, "\n"); if (cp != NULL) { *cp = '\0'; } // whole line is characters - no whitespace -- skip cp = strpbrk(p, " \t"); if ( cp == NULL ) { continue; } // NULL terminate address string *cp++ = '\0'; // // read address // - try IP4 // - ignore IP6 for now // - otherwise skip // DnsFileInfo.Ip4Address = inet_addr(p); if (DnsFileInfo.Ip4Address != INADDR_NONE || _strnicmp("255.255.255.255", p, 15) == 0) { break; } // invalid address, ignore line // // debug tracing // NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host file address %s", p ); continue; } // find the end of the string which was read ep = cp; while( *ep ) ep++; // // find name // - skip leading whitespace // - set global name ptr // while( *cp == ' ' || *cp == '\t' ) cp++; DnsFileInfo.pHostName = cp; // stop at trailing whitespace, NULL terminate cp = strpbrk(cp, " \t"); if ( cp != NULL ) { *cp++ = '\0'; } else { // we have a name - but no timestamp NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp", DnsFileInfo.pHostName ); goto Failed; } // we dont have any support for aliases // // find the timestamp // - skip leading whitespace // - read the time values // while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp != '#')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp", DnsFileInfo.pHostName ); goto Failed; } cp++; while( *cp == ' ' || *cp == '\t' ) cp++; // now at first system time value if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 1", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wYear = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 2", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wMonth = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 3", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wDayOfWeek = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 4", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wDay = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 5", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wHour = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 6", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wMinute = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 7", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wSecond = (WORD) atoi(cp); cp = strpbrk(cp, " \t"); if (cp == NULL) goto Failed; while( *cp == ' ' || *cp == '\t' ) cp++; if ((cp >= ep) || (*cp == '\0')) { NhTrace( TRACE_FLAG_DNS, "GetHostFromHostsIcsFile: Error parsing host (%s) file timestamp @ 8", DnsFileInfo.pHostName ); goto Failed; } DnsFileInfo.HostTime.wMilliseconds = (WORD) atoi(cp); // // successful entry read // /* NhTrace( TRACE_FLAG_DNS, "%s (%s) has timestamp: %04u-%02u-%02u %02u:%02u:%02u", DnsFileInfo.pHostName, inet_ntoa(*(PIN_ADDR)&DnsFileInfo.Ip4Address), DnsFileInfo.HostTime.wYear, DnsFileInfo.HostTime.wMonth, DnsFileInfo.HostTime.wDay, DnsFileInfo.HostTime.wHour, DnsFileInfo.HostTime.wMinute, DnsFileInfo.HostTime.wSecond ); */ return TRUE; Failed: // reset entries DnsFileInfo.HostLineBuf[0] = NULL; DnsFileInfo.pHostName = NULL; DnsFileInfo.Ip4Address = 0; ZeroMemory(&DnsFileInfo.HostTime, sizeof(SYSTEMTIME)); return TRUE; } // GetHostFromHostsIcsFile VOID LoadHostsIcsFile( BOOL fStartup ) /*++ Routine Description: Read hosts file into our local cache. Arguments: fStartup set to TRUE if called on startup of protocol Globals: DnsFileInfo.HostFile -- host file ptr, tested and set then cleared DnsFileInfo.HostTime -- host timestamp DnsFileInfo.pHostName -- name ptr is read DnsFileInfo.Ip4Address -- IP4 address is read Return Value: None. --*/ { register PCHAR * cp; FILETIME ftExpires; PWCHAR pszName; LPVOID lpMsgBuf; NhTrace( TRACE_FLAG_DNS, "LoadHostsIcsFile: Entering..." ); ACQUIRE_LOCK(&DnsFileInfo); // // read entries from host file until exhausted // DnsSetHostsIcsFile(FALSE); // read only while (GetHostFromHostsIcsFile(fStartup)) { if (DnsFileInfo.pHostName) { if (!SystemTimeToFileTime(&DnsFileInfo.HostTime, &ftExpires)) { DWORD dwLastError = GetLastError(); NhTrace( TRACE_FLAG_DNS, "LoadHostsIcsFile: SystemTimeToFileTime() failed" ); lpMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); NhTrace( TRACE_FLAG_DNS, "LoadHostsIcsFile: with message (0x%08x) %S", dwLastError, lpMsgBuf ); if (lpMsgBuf) LocalFree(lpMsgBuf); // skip entry continue; } pszName = (PWCHAR) NH_ALLOCATE((strlen(DnsFileInfo.pHostName) + 1) * sizeof(WCHAR)); mbstowcs(pszName, DnsFileInfo.pHostName, (strlen(DnsFileInfo.pHostName) + 1)); NhTrace( TRACE_FLAG_DNS, "%S (%s) has timestamp: %04u-%02u-%02u %02u:%02u:%02u", pszName, inet_ntoa(*(PIN_ADDR)&DnsFileInfo.Ip4Address), DnsFileInfo.HostTime.wYear, DnsFileInfo.HostTime.wMonth, DnsFileInfo.HostTime.wDay, DnsFileInfo.HostTime.wHour, DnsFileInfo.HostTime.wMinute, DnsFileInfo.HostTime.wSecond ); DnsAddAddressForName( pszName, DnsFileInfo.Ip4Address, ftExpires ); NH_FREE(pszName); } } DnsEndHostsIcsFile(); RELEASE_LOCK(&DnsFileInfo); // // now that we have everything in our table format, // write back a clean version to disk // SaveHostsIcsFile(FALSE); NhTrace( TRACE_FLAG_DNS, "LoadHostsIcsFile: ...Leaving." ); } // LoadHostsIcsFile VOID SaveHostsIcsFile( BOOL fShutdown ) /*++ Routine Description: Write hosts file from our local cache. Arguments: fShutdown set to TRUE if called on shutdown of protocol Globals: DnsFileInfo.HostFile -- host file ptr, tested and set then cleared DnsFileInfo.HostTime -- host timestamp DnsFileInfo.pHostName -- name ptr is read DnsFileInfo.Ip4Address -- IP4 address is read Return Value: None. --*/ { //DWORD dwSize = 0; //PWCHAR pszSuffix = NULL; UINT i; SYSTEMTIME stTime; LPVOID lpMsgBuf; NhTrace( TRACE_FLAG_DNS, "SaveHostsIcsFile: Entering..." ); // // adding ourself as a special case // DnsAddSelf(); // // get a copy of current ICS Domain suffix (used later on) // we dont play with it directly from the global copy // due to locking problems // /* EnterCriticalSection(&DnsGlobalInfoLock); if (DnsICSDomainSuffix) { dwSize = wcslen(DnsICSDomainSuffix) + 1; pszSuffix = reinterpret_cast( NH_ALLOCATE(sizeof(WCHAR) * dwSize) ); if (!pszSuffix) { NhTrace( TRACE_FLAG_DNS, "SaveHostsIcsFile: allocation failed for " "DnsICSDomainSuffix copy" ); } else { wcscpy(pszSuffix, DnsICSDomainSuffix); } } LeaveCriticalSection(&DnsGlobalInfoLock); */ ACQUIRE_LOCK(&DnsFileInfo); // // write entries into host file // DnsSetHostsIcsFile(TRUE); // overwrite existing file if any if (DnsFileInfo.HostFile != NULL) { // // write default header string // if (fShutdown) { // add extra # at front fputc('#', DnsFileInfo.HostFile); } fputs(HOSTSICSFILE_HEADER, DnsFileInfo.HostFile); PDNS_ENTRY pDnsEntry; EnterCriticalSection(&DnsTableLock); pDnsEntry = (PDNS_ENTRY) RtlEnumerateGenericTable(&g_DnsTable, TRUE); while (pDnsEntry != NULL) { for (i = 0; i < pDnsEntry->cAddresses; i++) { // // dont add entries with invalid suffixes // (this could happen for example because the suffix // was changed in the registry) // //if (!IsSuffixValid(pDnsEntry->pszName, pszSuffix)) //{ // continue; //} // // dont add expired entries to the hosts.ics file // if (IsFileTimeExpired(&pDnsEntry->aAddressInfo[i].ftExpires)) { continue; } if (!FileTimeToSystemTime( &pDnsEntry->aAddressInfo[i].ftExpires, &stTime)) { NhTrace( TRACE_FLAG_DNS, "SaveHostsIcsFile: FileTimeToSystemTime() failed" ); lpMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); NhTrace( TRACE_FLAG_DNS, "SaveHostsIcsFile: with message %S", lpMsgBuf ); if (lpMsgBuf) LocalFree(lpMsgBuf); // skip entry continue; } if (fShutdown) { // add extra # at front fputc('#', DnsFileInfo.HostFile); } fprintf( DnsFileInfo.HostFile, "%s %S # %u %u %u %u %u %u %u %u\n", inet_ntoa(*(PIN_ADDR)&pDnsEntry->aAddressInfo[i].ulAddress), pDnsEntry->pszName, stTime.wYear, stTime.wMonth, stTime.wDayOfWeek, stTime.wDay, stTime.wHour, stTime.wMinute, stTime.wSecond, stTime.wMilliseconds ); NhTrace( TRACE_FLAG_DNS, "adding entry: %s %S # %u %u %u %u %u %u %u %u\n", inet_ntoa(*(PIN_ADDR)&pDnsEntry->aAddressInfo[i].ulAddress), pDnsEntry->pszName, stTime.wYear, stTime.wMonth, stTime.wDayOfWeek, stTime.wDay, stTime.wHour, stTime.wMinute, stTime.wSecond, stTime.wMilliseconds ); } pDnsEntry = (PDNS_ENTRY) RtlEnumerateGenericTable(&g_DnsTable, FALSE); } LeaveCriticalSection(&DnsTableLock); } DnsEndHostsIcsFile(); RELEASE_LOCK(&DnsFileInfo); /* if (pszSuffix) { NH_FREE(pszSuffix); pszSuffix = NULL; } */ NhTrace( TRACE_FLAG_DNS, "SaveHostsIcsFile: ...Leaving." ); } // SaveHostsIcsFile BOOL IsFileTimeExpired( FILETIME *ftTime ) { ULARGE_INTEGER uliTime, uliNow; FILETIME ftNow; GetSystemTimeAsFileTime(&ftNow); memcpy(&uliNow, &ftNow, sizeof(ULARGE_INTEGER)); memcpy(&uliTime, ftTime, sizeof(ULARGE_INTEGER)); return (uliTime.QuadPart < uliNow.QuadPart); } // IsFileTimeExpired BOOL IsSuffixValid( WCHAR *pszName, WCHAR *pszSuffix ) /*++ Routine Description: This routine is invoked to compare suffix on the end of the name with what the DNS component thinks of as the current suffix. Arguments: none. Return Value: TRUE or FALSE. Environment: Invoked in an arbitrary context. --*/ { BOOL ret; PWCHAR start = pszName; size_t lenName = 0, lenSuffix = 0; if (!start) { return FALSE; } lenName = wcslen(pszName); lenSuffix = wcslen(pszSuffix); if (!lenName || !lenSuffix) { return FALSE; } if (lenName < lenSuffix) { return FALSE; } lenName -= lenSuffix; while (lenName--) start++; ret = _wcsicmp(start, pszSuffix); return (!ret); } // IsSuffixValid // // End dnsfile.c //