/*++ Copyright (c) 1991-1993 Microsoft Corporation Module Name: parse.c Abstract: This source contains the functions that parse the lmhosts file. Author: Jim Stewart May 2, 1993 Revision History: --*/ #include "precomp.h" #include "hosts.h" #include #include #include "parse.tmh" #ifdef VXD extern BOOL fInInit; extern BOOLEAN CachePrimed; #endif // // Returns 0 if equal, 1 if not equal. Used to avoid using c-runtime // #define strncmp( pch1, pch2, length ) \ (!CTEMemEqu( pch1, pch2, length ) ) // // Private Definitions // // As an lmhosts file is parsed, a #INCLUDE directive is interpreted // according to the INCLUDE_STATE at that instance. This state is // determined by the #BEGIN_ALTERNATE and #END_ALTERNATE directives. // // typedef enum _INCLUDE_STATE { MustInclude = 0, // shouldn't fail TryToInclude, // in alternate block SkipInclude // satisfied alternate // block } INCLUDE_STATE; // // LmpGetTokens() parses a line and returns the tokens in the following // order: // typedef enum _TOKEN_ORDER_ { IpAddress = 0, // first token NbName, // 2nd token GroupName, // 3rd or 4th token NotUsed, // #PRE, if any NotUsed2, // #NOFNR, if any MaxTokens // this must be last } TOKEN_ORDER; // // As each line in an lmhosts file is parsed, it is classified into one of // the categories enumerated below. // // However, Preload is a special member of the enum. // // typedef enum _TYPE_OF_LINE { Comment = 0x0000, // comment line Ordinary = 0x0001, // ip_addr NetBIOS name Domain = 0x0002, // ... #DOM:name Include = 0x0003, // #INCLUDE file BeginAlternate = 0x0004, // #BEGIN_ALTERNATE EndAlternate = 0x0005, // #END_ALTERNATE ErrorLine = 0x0006, // Error in line NoFNR = 0x4000, // ... #NOFNR Preload = 0x8000 // ... #PRE } TYPE_OF_LINE; // // In an lmhosts file, the following are recognized as keywords: // // #BEGIN_ALTERNATE #END_ALTERNATE #PRE // #DOM: #INCLUDE // // Information about each keyword is kept in a KEYWORD structure. // // typedef struct _KEYWORD { // reserved keyword char *k_string; // NULL terminated size_t k_strlen; // length of token TYPE_OF_LINE k_type; // type of line int k_noperands; // max operands on line } KEYWORD, *PKEYWORD; typedef struct _LINE_CHARACTERISTICS_ { int l_category:4; // enum _TYPE_OF_LINE int l_preload:1; // marked with #PRE ? unsigned int l_nofnr:1; // marked with #NOFNR } LINE_CHARACTERISTICS, *PLINE_CHARACTERISTICS; // // Do not allow DNS name queries for the following Name Types: // // Name Number(h) Type Usage // -------------------------------------------------------------------------- // 01 Unique Messenger Service // <\\--__MSBROWSE__> 01 Group Master Browser // 1B Unique Domain Master Browser // 1C Group Domain Controllers // 1C Group IIS // 1D Unique Master Browser // 1E Group Browser Service Elections #define IsValidDnsNameTag(_c) \ ((_c != 0x01) && \ ((_c < 0x1B) || (_c > 0x1E))) // // Local Variables // // // In an lmhosts file, the token '#' in any column usually denotes that // the rest of the line is to be ignored. However, a '#' may also be the // first character of a keyword. // // Keywords are divided into two groups: // // 1. decorations that must either be the 3rd or 4th token of a line, // 2. directives that must begin in column 0, // // KEYWORD Decoration[] = { DOMAIN_TOKEN, sizeof(DOMAIN_TOKEN) - 1, Domain, 5, PRELOAD_TOKEN, sizeof(PRELOAD_TOKEN) - 1, Preload, 5, NOFNR_TOKEN, sizeof(NOFNR_TOKEN) -1, NoFNR, 5, NULL, 0 // must be last }; KEYWORD Directive[] = { INCLUDE_TOKEN, sizeof(INCLUDE_TOKEN) - 1, Include, 2, BEG_ALT_TOKEN, sizeof(BEG_ALT_TOKEN) - 1, BeginAlternate, 1, END_ALT_TOKEN, sizeof(END_ALT_TOKEN) - 1, EndAlternate, 1, NULL, 0 // must be last }; // // Local Variables // // // Each preloaded lmhosts entry corresponds to NSUFFIXES NetBIOS names, // each with a 16th byte from Suffix[]. // // For example, an lmhosts entry specifying "popcorn" causes the // following NetBIOS names to be added to nbt.sys' name cache: // // "POPCORN " // "POPCORN 0x0" // "POPCORN 0x3" // // #define NSUFFIXES 3 UCHAR Suffix[] = { // LAN Manager Component 0x20, // server 0x0, // redirector 0x03 // messenger }; #ifndef VXD // // this structure tracks names queries that are passed up to user mode // to resolve via DnsQueries // tLMHSVC_REQUESTS DnsQueries; tLMHSVC_REQUESTS CheckAddr; #endif tLMHSVC_REQUESTS LmHostQueries; // Track names queries passed for LMhost processing tDOMAIN_LIST DomainNames; // // Local (Private) Functions // LINE_CHARACTERISTICS LmpGetTokens ( IN OUT PUCHAR line, OUT PUCHAR *token, IN OUT int *pnumtokens ); PKEYWORD LmpIsKeyWord ( IN PUCHAR string, IN PKEYWORD table ); BOOLEAN LmpBreakRecursion( IN PUCHAR path, IN PUCHAR target, IN ULONG TargetLength ); LONG HandleSpecial( IN char **pch); ULONG AddToDomainList ( IN PUCHAR pName, IN tIPADDRESS IpAddress, IN PLIST_ENTRY pDomainHead, IN BOOLEAN fPreload ); NTSTATUS ChangeStateOfName ( IN tIPADDRESS IpAddress, IN NBT_WORK_ITEM_CONTEXT *pContext, IN OUT NBT_WORK_ITEM_CONTEXT **ppContext, IN USHORT NameAddFlags ); VOID LmHostTimeout( PVOID pContext, PVOID pContext2, tTIMERQENTRY *pTimerQEntry ); NBT_WORK_ITEM_CONTEXT * GetNameToFind( OUT PUCHAR pName ); VOID GetContext ( IN OUT NBT_WORK_ITEM_CONTEXT **ppContext ); VOID MakeNewListCurrent ( PLIST_ENTRY pTmpDomainList ); VOID RemoveNameAndCompleteReq ( IN NBT_WORK_ITEM_CONTEXT *pContext, IN NTSTATUS status ); PCHAR Nbtstrcat( PUCHAR pch, PUCHAR pCat, LONG Len ); //******************* Pageable Routine Declarations **************** #ifdef ALLOC_PRAGMA #pragma CTEMakePageable(PAGE, LmGetIpAddr) #pragma CTEMakePageable(PAGE, HandleSpecial) #pragma CTEMakePageable(PAGE, LmpGetTokens) #pragma CTEMakePageable(PAGE, LmpIsKeyWord) #pragma CTEMakePageable(PAGE, LmpBreakRecursion) #pragma CTEMakePageable(PAGE, AddToDomainList) #pragma CTEMakePageable(PAGE, LmExpandName) #pragma CTEMakePageable(PAGE, LmInclude) #pragma CTEMakePageable(PAGE, LmGetFullPath) #pragma CTEMakePageable(PAGE, PrimeCache) #pragma CTEMakePageable(PAGE, DelayedScanLmHostFile) #pragma CTEMakePageable(PAGE, NbtCompleteLmhSvcRequest) #endif //******************* Pageable Routine Declarations **************** //---------------------------------------------------------------------------- unsigned long LmGetIpAddr ( IN PUCHAR path, IN PUCHAR target, IN CHAR RecurseDepth, OUT BOOLEAN *bFindName ) /*++ Routine Description: This function searches the file for an lmhosts entry that can be mapped to the second level encoding. It then returns the ip address specified in that entry. This function is called recursively, via LmInclude() !! Arguments: path - a fully specified path to a lmhosts file target - the unencoded 16 byte NetBIOS name to look for RecurseDepth- the depth to which we can resurse -- 0 => no more recursion Return Value: The ip address (network byte order), or 0 if no appropriate entry was found. Note that in most contexts (but not here), ip address 0 signifies "this host." --*/ { PUCHAR buffer; PLM_FILE pfile; NTSTATUS status; int count, nwords; INCLUDE_STATE incstate; PUCHAR token[MaxTokens]; LINE_CHARACTERISTICS current; unsigned long inaddr, retval; UCHAR temp[NETBIOS_NAME_SIZE+1]; CTEPagedCode(); // // Check for infinitely recursive name lookup in a #INCLUDE. // if (LmpBreakRecursion(path, target, NETBIOS_NAME_SIZE-1) == TRUE) { return (0); } #ifdef VXD // // if we came here via nbtstat -R and InDos is set, report error: user // can try nbtstat -R again. (since nbtstat can only be run from DOS box, // can InDos be ever set??? Might as well play safe) // if ( !fInInit && GetInDosFlag() ) { return(0); } #endif pfile = LmOpenFile(path); if (!pfile) { return((unsigned long) 0); } *bFindName = FALSE; inaddr = 0; incstate = MustInclude; while (buffer = LmFgets(pfile, &count)) { nwords = MaxTokens; current = LmpGetTokens(buffer, token, &nwords); switch ((ULONG)current.l_category) { case ErrorLine: continue; case Domain: case Ordinary: if (current.l_preload || ((nwords - 1) < NbName)) { continue; } break; case Include: if (!RecurseDepth || (incstate == SkipInclude) || (nwords < 2)) { continue; } retval = LmInclude(token[1], LmGetIpAddr, target, (CHAR) (RecurseDepth-1), bFindName); if (retval != 0) { if (incstate == TryToInclude) { incstate = SkipInclude; } } else { if (incstate == MustInclude) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.LmGetIpAddr: Can't #INCLUDE \"%s\"", token[1])); } continue; } inaddr = retval; goto found; case BeginAlternate: ASSERT(nwords == 1); incstate = TryToInclude; continue; case EndAlternate: ASSERT(nwords == 1); incstate = MustInclude; continue; default: continue; } if (strlen(token[NbName]) == (NETBIOS_NAME_SIZE)) { if (strncmp(token[NbName], target, (NETBIOS_NAME_SIZE)) != 0) { continue; } } else { // // attempt to match, in a case insensitive manner, the first 15 // bytes of the lmhosts entry with the target name. // LmExpandName(temp, token[NbName], 0); if (strncmp(temp, target, NETBIOS_NAME_SIZE - 1) != 0) { continue; } } if (current.l_nofnr) { *bFindName = TRUE; } status = ConvertDottedDecimalToUlong(token[IpAddress],&inaddr); if (!NT_SUCCESS(status)) { inaddr = 0; } break; } found: status = LmCloseFile(pfile); ASSERT(status == STATUS_SUCCESS); if (!NT_SUCCESS(status)) { *bFindName = FALSE; } IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.LmGetIpAddr: (\"%15.15s<%X>\") = %X\n",target,target[15],inaddr)); return(inaddr); } // LmGetIpAddr //---------------------------------------------------------------------------- LONG HandleSpecial( IN CHAR **pch) /*++ Routine Description: This function converts ASCII hex into a ULONG. Arguments: Return Value: The ip address (network byte order), or 0 if no appropriate entry was found. Note that in most contexts (but not here), ip address 0 signifies "this host." --*/ { int sval; int rval; char *sp = *pch; int i; CTEPagedCode(); sp++; switch (*sp) { case '\\': // the second character is also a \ so return a \ and set pch to // point to the next character (\) // *pch = sp; return((int)'\\'); default: // convert some number of characters to hex and increment pch // the expected format is "\0x03" // // sscanf(sp, "%2x%n", &sval, &rval); sval = 0; rval = 0; sp++; // check for the 0x part of the hex number if (*sp != 'x') { *pch = sp; return(-1); } sp++; for (i=0;(( i<2 ) && *sp) ;i++ ) { if (*sp != ' ') { // convert from ASCII to hex, allowing capitals too // if (*sp >= 'a') { sval = *sp - 'a' + 10 + sval*16; } else if (*sp >= 'A') { sval = *sp - 'A' + 10 + sval*16; } else { sval = *sp - '0' + sval*16; } sp++; rval++; } else break; } if (rval < 1) { *pch = sp; return(-1); } *pch += (rval+2); // remember to account for the characters 0 and x return(sval); } } #define LMHASSERT(s) if (!(s)) \ { retval.l_category = ErrorLine; return(retval); } //---------------------------------------------------------------------------- LINE_CHARACTERISTICS LmpGetTokens ( IN OUT PUCHAR line, OUT PUCHAR *token, IN OUT int *pnumtokens ) /*++ Routine Description: This function parses a line for tokens. A maximum of *pnumtokens are collected. Arguments: line - pointer to the NULL terminated line to parse token - an array of pointers to tokens collected *pnumtokens - on input, number of elements in the array, token[]; on output, number of tokens collected in token[] Return Value: The characteristics of this lmhosts line. Notes: 1. Each token must be separated by white space. Hence, the keyword "#PRE" in the following line won't be recognized: 11.1.12.132 lothair#PRE 2. Any ordinary line can be decorated with a "#PRE", a "#DOM:name" or both. Hence, the following lines must all be recognized: 111.21.112.3 kernel #DOM:ntwins #PRE 111.21.112.4 orville #PRE #DOM:ntdev 111.21.112.7 cliffv4 #DOM:ntlan 111.21.112.132 lothair #PRE --*/ { enum _PARSE { // current fsm state StartofLine, WhiteSpace, AmidstToken } state; PUCHAR pch; // current fsm input PUCHAR och; PKEYWORD keyword; int index, maxtokens, quoted, rchar; LINE_CHARACTERISTICS retval; CTEPagedCode(); CTEZeroMemory(token, *pnumtokens * sizeof(PUCHAR *)); state = StartofLine; retval.l_category = Ordinary; retval.l_preload = 0; retval.l_nofnr = 0; maxtokens = *pnumtokens; index = 0; quoted = 0; for (pch = line; *pch; pch++) { switch (*pch) { // // does the '#' signify the start of a reserved keyword, or the // start of a comment ? // // case '#': if (quoted) { *och++ = *pch; continue; } keyword = LmpIsKeyWord( pch, (state == StartofLine) ? Directive : Decoration); if (keyword) { state = AmidstToken; maxtokens = keyword->k_noperands; switch (keyword->k_type) { case NoFNR: retval.l_nofnr = 1; continue; case Preload: retval.l_preload = 1; continue; default: LMHASSERT(maxtokens <= *pnumtokens); LMHASSERT(index < maxtokens); token[index++] = pch; retval.l_category = keyword->k_type; continue; } LMHASSERT(0); } if (state == StartofLine) { retval.l_category = Comment; } /* fall through */ case '\r': case '\n': *pch = (UCHAR) NULL; if (quoted) { *och = (UCHAR) NULL; } goto done; case ' ': case '\t': if (quoted) { *och++ = *pch; continue; } if (state == AmidstToken) { state = WhiteSpace; *pch = (UCHAR) NULL; if (index == maxtokens) { goto done; } } continue; case '"': if ((state == AmidstToken) && quoted) { state = WhiteSpace; quoted = 0; *pch = (UCHAR) NULL; *och = (UCHAR) NULL; if (index == maxtokens) { goto done; } continue; } state = AmidstToken; quoted = 1; LMHASSERT(maxtokens <= *pnumtokens); LMHASSERT(index < maxtokens); token[index++] = pch + 1; och = pch + 1; continue; case '\\': if (quoted) { rchar = HandleSpecial(&pch); if (rchar == -1) { retval.l_category = ErrorLine; return(retval); } *och++ = (UCHAR)rchar; // // put null on end of string // continue; } default: if (quoted) { *och++ = *pch; continue; } if (state == AmidstToken) { continue; } state = AmidstToken; LMHASSERT(maxtokens <= *pnumtokens); LMHASSERT(index < maxtokens); token[index++] = pch; continue; } } done: // // if there is no name on the line, then return an error // if (index <= NbName && index != maxtokens) { retval.l_category = ErrorLine; } ASSERT(!*pch); ASSERT(maxtokens <= *pnumtokens); ASSERT(index <= *pnumtokens); *pnumtokens = index; return(retval); } // LmpGetTokens //---------------------------------------------------------------------------- PKEYWORD LmpIsKeyWord ( IN PUCHAR string, IN PKEYWORD table ) /*++ Routine Description: This function determines whether the string is a reserved keyword. Arguments: string - the string to search table - an array of keywords to look for Return Value: A pointer to the relevant keyword object, or NULL if unsuccessful --*/ { size_t limit; PKEYWORD special; CTEPagedCode(); limit = strlen(string); for (special = table; special->k_string; special++) { if (limit < special->k_strlen) { continue; } if ((limit >= special->k_strlen) && !strncmp(string, special->k_string, special->k_strlen)) { return(special); } } return((PKEYWORD) NULL); } // LmpIsKeyWord //---------------------------------------------------------------------------- BOOLEAN LmpBreakRecursion( IN PUCHAR path, IN PUCHAR target, IN ULONG TargetLength ) /*++ Routine Description: This function checks that the file name we are about to open does not use the target name of this search, which would cause an infinite lookup loop. Arguments: path - a fully specified path to a lmhosts file target - the unencoded 16 byte NetBIOS name to look for Return Value: TRUE if the UNC server name in the file path is the same as the target of this search. FALSE otherwise. Notes: This function does not detect redirected drives. --*/ { PCHAR keystring = "\\DosDevices\\UNC\\"; PCHAR servername[NETBIOS_NAME_SIZE+1]; // for null on end PCHAR marker1; PCHAR marker2; PCHAR marker3; BOOLEAN retval = FALSE; tNAMEADDR *pNameAddr; ULONG uType; CTEPagedCode(); // // Check for and extract the UNC server name // if ((path) && (strlen(path) > strlen(keystring))) { // check that the name is a unc name if (strncmp(path, keystring, strlen(keystring)) == 0) { marker1 = path + strlen(keystring); // the end of the \DosDevices\Unc\ string marker3 = &path[strlen(path)-1]; // the end of the whole path marker2 = strchr(marker1,'\\'); // the end of the server name if ((marker2) && // marker2 can be NULL if '\\' does not exist in the string (marker2 != marker3)) { *marker2 = '\0'; // // attempt to match, in a case insensitive manner, the // first 15 bytes of the lmhosts entry with the target // name. // LmExpandName((PUCHAR)servername, marker1, 0); if(strncmp((PUCHAR)servername, target, TargetLength) == 0) { // // break the recursion // retval = TRUE; IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.LmpBreakRecursion: Not including Lmhosts file <%s> because of recursive name\n", servername)); } else { // // check if the name has been preloaded in the cache, and // if not, fail the request so we can't get into a loop // trying to include the remote file while trying to // resolve the remote name // pNameAddr = LockAndFindName(NBT_REMOTE, (PCHAR)servername, NbtConfig.pScope, &uType); if (!pNameAddr || !(pNameAddr->NameTypeState & PRELOADED) ) { // // break the recursion // retval = TRUE; IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.LmpBreakRecursion: Not including Lmhosts #include because name not Preloaded %s\n", servername)); } } *marker2 = '\\'; } } } return(retval); } //---------------------------------------------------------------------------- char * LmExpandName ( OUT PUCHAR dest, IN PUCHAR source, IN UCHAR last ) /*++ Routine Description: This function expands an lmhosts entry into a full 16 byte NetBIOS name. It is padded with blanks up to 15 bytes; the 16th byte is the input parameter, last. This function does not encode 1st level names to 2nd level names nor vice-versa. Both dest and source are NULL terminated strings. Arguments: dest - sizeof(dest) must be NBT_NONCODED_NMSZ source - the lmhosts entry last - the 16th byte of the NetBIOS name Return Value: dest. --*/ { char byte; char *retval = dest; char *src = source ; #ifndef VXD WCHAR unicodebuf[NETBIOS_NAME_SIZE+1]; UNICODE_STRING unicode; STRING tmp; #endif NTSTATUS status; PUCHAR limit; CTEPagedCode(); // // first, copy the source OEM string to the destination, pad it, and // add the last character. // limit = dest + NETBIOS_NAME_SIZE - 1; while ( (*source != '\0') && (dest < limit) ) { *dest++ = *source++; } while(dest < limit) { *dest++ = ' '; } ASSERT(dest == (retval + NETBIOS_NAME_SIZE - 1)); *dest = '\0'; *(dest + 1) = '\0'; dest = retval; #ifndef VXD // // Now, convert to unicode then to ANSI to force the OEM -> ANSI munge. // Then convert back to Unicode and uppercase the name. Finally convert // back to OEM. // unicode.Length = 0; unicode.MaximumLength = 2*(NETBIOS_NAME_SIZE+1); unicode.Buffer = unicodebuf; RtlInitString(&tmp, dest); status = RtlOemStringToUnicodeString(&unicode, &tmp, FALSE); if (!NT_SUCCESS(status)) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint (("Nbt.LmExpandName: Oem -> Unicode failed, status %X\n", status)); goto oldupcase; } status = RtlUnicodeStringToAnsiString(&tmp, &unicode, FALSE); if (!NT_SUCCESS(status)) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint (("Nbt.LmExpandName: Unicode -> Ansi failed, status %X\n", status)); goto oldupcase; } status = RtlAnsiStringToUnicodeString(&unicode, &tmp, FALSE); if (!NT_SUCCESS(status)) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint (("Nbt.LmExpandName: Ansi -> Unicode failed, status %X\n", status)); goto oldupcase; } status = RtlUpcaseUnicodeStringToOemString(&tmp, &unicode, FALSE); if (!NT_SUCCESS(status)) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint (("Nbt.LmExpandName: Unicode upcase -> Oem failed, status %X\n", status)); goto oldupcase; } // write the last byte to "0x20" or "0x03" or whatever // since we do not want it to go through the munge above. // dest[NETBIOS_NAME_SIZE-1] = last; return(retval); #endif oldupcase: for ( source = src ; dest < (retval + NETBIOS_NAME_SIZE - 1); dest++) { byte = *(source++); if (!byte) { break; } // Don't use the c-runtime (nt c defn. included first) // What about extended characters etc.? Since extended characters do // not normally part of netbios names, we will fix if requested *dest = (byte >= 'a' && byte <= 'z') ? byte-'a' + 'A' : byte ; // *dest = islower(byte) ? toupper(byte) : byte; } for (; dest < retval + NETBIOS_NAME_SIZE - 1; dest++) { *dest = ' '; } ASSERT(dest == (retval + NETBIOS_NAME_SIZE - 1)); *dest = last; *(dest + 1) = (char) NULL; return(retval); } // LmExpandName //---------------------------------------------------------------------------- unsigned long LmInclude( IN PUCHAR file, IN LM_PARSE_FUNCTION function, IN PUCHAR argument OPTIONAL, IN CHAR RecurseDepth, OUT BOOLEAN *NoFindName OPTIONAL ) /*++ Routine Description: LmInclude() is called to process a #INCLUDE directive in the lmhosts file. Arguments: file - the file to include function - function to parse the included file argument - optional second argument to the parse function RecurseDepth- the depth to which we can resurse -- 0 => no more recursion NoFindName - Are find names allowed for this address Return Value: The return value from the parse function. This should be -1 if the file could not be processed, or else some positive number. --*/ { int retval; PUCHAR end; NTSTATUS status; PUCHAR path; CTEPagedCode(); // // unlike C, treat both variations of the #INCLUDE directive identically: // // #INCLUDE file // #INCLUDE "file" // // If a leading '"' exists, skip over it. // if (*file == '"') { file++; end = strchr(file, '"'); if (end) { *end = (UCHAR) NULL; } } // // check that the file to be included has been preloaded in the cache // since we do not want to have the name query come right back to here // to force another inclusion of the same remote file // #ifdef VXD return (*function)(file, argument, RecurseDepth, NoFindName ) ; #else status = LmGetFullPath(file, &path); if (status != STATUS_SUCCESS) { return(status); } // IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.LmInclude: #INCLUDE \"%s\"\n", path)); retval = (*function) (path, argument, RecurseDepth, NoFindName); CTEMemFree(path); return(retval); #endif } // LmInclude #ifndef VXD // Not used by VXD //---------------------------------------------------------------------------- NTSTATUS LmGetFullPath ( IN PUCHAR target, OUT PUCHAR *ppath ) /*++ Routine Description: This function returns the full path of the lmhosts file. This is done by forming a string from the concatenation of the C strings DatabasePath and the string, file. Arguments: target - the name of the file. This can either be a full path name or a mere file name. path - a pointer to a UCHAR Return Value: STATUS_SUCCESS if successful. Notes: RtlMoveMemory() handles overlapped copies; RtlCopyMemory() doesn't. --*/ { ULONG FileNameType; ULONG Len; PUCHAR path; CTEPagedCode(); // // use a count to figure out what sort of string to build up // // 0 - local full path file name // 1 - local file name only, no path // 2 - remote file name // 3 - \SystemRoot\ starting file name, or \DosDevices\UNC\... // // if the target begins with a '\', or contains a DOS drive letter, // then assume that it specifies a full path. Otherwise, prepend the // directory used to specify the lmhost file itself. // // if (target[1] == ':') { FileNameType = 0; } else if (strncmp(&target[1],"SystemRoot",10) == 0) { FileNameType = 3; } else if (strncmp(&target[0],"\\DosDevices\\",12) == 0) { FileNameType = 3; } else if (strncmp(target,"\\DosDevices\\UNC\\",sizeof("\\DosDevices\\UNC\\")-1) == 0) { FileNameType = 3; } else { FileNameType = 1; } // // does the directory specify a remote file ? // // If so, it must be prefixed with "\\DosDevices\\UNC", and the double // slashes of the UNC name eliminated. // // if ((target[1] == '\\') && (target[0] == '\\')) { FileNameType = 2; } path = NULL; switch (FileNameType) { case 0: // // Full file name, put \DosDevices on front of name // Len = sizeof("\\DosDevices\\") + strlen(target); path = NbtAllocMem (Len, NBT_TAG2('11')); if (path) { ULONG Length=sizeof("\\DosDevices\\"); // Took out -1 strncpy(path,"\\DosDevices\\",Length); Nbtstrcat(path,target,Len); } break; case 1: // // only the file name is present, with no path, so use the path // specified for the lmhost file in the registry NbtConfig.PathLength // includes the last backslash of the path. // //Len = sizeof("\\DosDevices\\") + NbtConfig.PathLength + strlen(target); CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); // # 247429 Len = NbtConfig.PathLength + strlen(target) +1; path = NbtAllocMem (Len, NBT_TAG2('12')); if (path) { //ULONG Length=sizeof("\\DosDevices") -1; // -1 not to count null //strncpy(path,"\\DosDevices",Length); strncpy(path,NbtConfig.pLmHosts,NbtConfig.PathLength); path[NbtConfig.PathLength] = '\0'; Nbtstrcat(path,target,Len); } CTEExReleaseResource(&NbtConfig.Resource); break; case 2: // // Full file name, put \DosDevices\UNC on front of name and delete // one of the two back slashes used for the remote name // Len = strlen(target); path = NbtAllocMem (Len+sizeof("\\DosDevices\\UNC"), NBT_TAG2('13')); if (path) { ULONG Length = sizeof("\\DosDevices\\UNC"); strncpy(path,"\\DosDevices\\UNC",Length); // to delete the first \ from the two \\ on the front of the // remote file name add one to target. // Nbtstrcat(path,target+1,Len+sizeof("\\DosDevices\\UNC")); } break; case 3: // the target is the full path Len = strlen(target) + 1; path = NbtAllocMem (Len, NBT_TAG2('14')); if (path) { strncpy(path,target,Len); } break; } if (path) { *ppath = path; return(STATUS_SUCCESS); } else return(STATUS_UNSUCCESSFUL); } // LmGetFullPath //---------------------------------------------------------------------------- VOID DelayedScanLmHostFile ( IN tDGRAM_SEND_TRACKING *pUnused1, IN PVOID pUnused2, IN PVOID pUnused3, IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This function is called by the Executive Worker thread to scan the LmHost file looking for a name. The name to query is on a list in the DNSQueries structure. Arguments: Context - Return Value: none --*/ { NTSTATUS status; LONG IpAddress; ULONG IpAddrsList[2]; BOOLEAN bFound; NBT_WORK_ITEM_CONTEXT *pContext; BOOLEAN DoingDnsResolve = FALSE; UCHAR pName[NETBIOS_NAME_SIZE]; PUCHAR LmHostsPath; ULONG LoopCount; tDGRAM_SEND_TRACKING *pTracker; tDGRAM_SEND_TRACKING *pTracker0; CTEPagedCode(); LoopCount = 0; while (TRUE) { // get the next name on the linked list of LmHost name queries that // are pending // pContext = NULL; DoingDnsResolve = FALSE; if (!(pContext = GetNameToFind(pName))) { return; } LoopCount ++; IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.DelayedScanLmHostFile: Lmhosts pName = %15.15s<%X>,LoopCount=%X\n", pName,pName[15],LoopCount)); status = STATUS_TIMEOUT; // // check if the name is in the lmhosts file or pass to Dns if // DNS is enabled // IpAddress = 0; if (NbtConfig.EnableLmHosts) { #ifdef VXD // // if for some reason PrimeCache failed at startup time // then this is when we retry. // if (!CachePrimed) { if (PrimeCache (NbtConfig.pLmHosts, NULL, MAX_RECURSE_DEPTH, NULL) != -1) { CachePrimed = TRUE ; } } #endif // // The NbtConfig.pLmHosts path can change if the registry is // read during this interval // We cannot acquire the ResourceLock here since reading the // LmHosts file might result in File operations + network reads // that could cause a deadlock (Worker threads / ResourceLock)! // Best solution at this time is to copy the path onto a local // buffer under the Resource lock, and then try to read the file! // CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE); if ((NbtConfig.pLmHosts) && (LmHostsPath = NbtAllocMem ((strlen(NbtConfig.pLmHosts)+1), NBT_TAG2('20')))) { CTEMemCopy (LmHostsPath, NbtConfig.pLmHosts, (strlen(NbtConfig.pLmHosts)+1)); CTEExReleaseResource(&NbtConfig.Resource); IpAddress = LmGetIpAddr(LmHostsPath, pName, 1, &bFound); CTEMemFree(LmHostsPath); } else { CTEExReleaseResource(&NbtConfig.Resource); IpAddress = 0; } #ifdef VXD // // hmmm.. didn't find it in lmhosts: try hosts (if Dns is enabled) // if ((IpAddress == (ULONG)0) && (NbtConfig.ResolveWithDns)) { IpAddress = LmGetIpAddr(NbtConfig.pHosts, pName, 1, &bFound); } #endif } if (IpAddress == (ULONG)0) { // check if the name query has been cancelled // LOCATION(0x61); GetContext (&pContext); // // for some reason we didn't find our context: maybe cancelled. // Go back to the big while loop... // if (!pContext) { continue; } // // see if the name is in the 11.101.4.26 format: if so, we got the // ipaddr! Use that ipaddr to get the server name // pTracker = ((NBT_WORK_ITEM_CONTEXT *)pContext)->pTracker; pTracker0 = (tDGRAM_SEND_TRACKING *)((NBT_WORK_ITEM_CONTEXT *)pContext)->pClientContext; if (pTracker0->Flags & (REMOTE_ADAPTER_STAT_FLAG|SESSION_SETUP_FLAG|DGRAM_SEND_FLAG)) { IpAddress = Nbt_inet_addr(pTracker->pNameAddr->Name, pTracker0->Flags); } // // yes, the name is the ipaddr: NbtCompleteLmhSvcRequest() starts // the process of finding out server name for this ipaddr // if (IpAddress) { IpAddrsList[0] = IpAddress; IpAddrsList[1] = 0; // // if this is in response to an adapter stat command (e.g.nbtstat -a) then // don't try to find the server name (using remote adapter status!) // if (pTracker0->Flags & REMOTE_ADAPTER_STAT_FLAG) { // // change the state to resolved if the name query is still pending // status = ChangeStateOfName(IpAddress, pContext, &pContext, NAME_RESOLVED_BY_IP); } else { NbtCompleteLmhSvcRequest(pContext, IpAddrsList, NBT_RESOLVE_WITH_DNS, 0, NULL, TRUE); // // done with this name query: go back to the big while loop // continue; } } // // // inet_addr failed. If DNS resolution is enabled, try DNS else if ((NbtConfig.ResolveWithDns) && (!(pTracker0->Flags & NO_DNS_RESOLUTION_FLAG))) { status = NbtProcessLmhSvcRequest (pContext, NBT_RESOLVE_WITH_DNS); if (NT_SUCCESS(status)) { DoingDnsResolve = TRUE; } } } else // if (IpAddress != (ULONG)0) { // // change the state to resolved if the name query is still pending // status = ChangeStateOfName(IpAddress, NULL, &pContext, NAME_RESOLVED_BY_LMH); } // // if DNS gets involved, then we wait for that to complete before calling // completion routine. // if (!DoingDnsResolve) { LOCATION(0x60); RemoveNameAndCompleteReq((NBT_WORK_ITEM_CONTEXT *)pContext, status); } }// of while(TRUE) } //---------------------------------------------------------------------------- ULONG AddToDomainList ( IN PUCHAR pName, IN tIPADDRESS IpAddress, IN PLIST_ENTRY pDomainHead, IN BOOLEAN fPreload ) /*++ Routine Description: This function adds a name and ip address to the list of domains that are stored in a list. Arguments: Return Value: --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tNAMEADDR *pNameAddr=NULL; tIPADDRESS *pIpAddr; CTEPagedCode(); pHead = pEntry = pDomainHead; if (!IsListEmpty(pDomainHead)) { pNameAddr = FindInDomainList(pName,pDomainHead); if (pNameAddr) { // // the name matches, so add to the end of the ip address list // if (pNameAddr->CurrentLength < pNameAddr->MaxDomainAddrLength) { pIpAddr = pNameAddr->pLmhSvcGroupList; while (*pIpAddr != (ULONG)-1) { pIpAddr++; } *pIpAddr++ = IpAddress; *pIpAddr = (ULONG)-1; pNameAddr->CurrentLength += sizeof(ULONG); } else { // // need to allocate more memory for for ip addresses // if (pIpAddr = NbtAllocMem (pNameAddr->MaxDomainAddrLength+INITIAL_DOM_SIZE, NBT_TAG2('08'))) { CTEMemCopy(pIpAddr, pNameAddr->pLmhSvcGroupList, pNameAddr->MaxDomainAddrLength); // // Free the old chunk of memory and tack the new one on // to the pNameaddr // CTEMemFree(pNameAddr->pLmhSvcGroupList); pNameAddr->pLmhSvcGroupList = pIpAddr; pIpAddr = (PULONG)((PUCHAR)pIpAddr + pNameAddr->MaxDomainAddrLength); // // our last entry was -1: overwrite that one // pIpAddr--; *pIpAddr++ = IpAddress; *pIpAddr = (ULONG)-1; // // update the number of addresses in the list so far // pNameAddr->MaxDomainAddrLength += INITIAL_DOM_SIZE; pNameAddr->CurrentLength += sizeof(ULONG); pNameAddr->Verify = REMOTE_NAME; } } } } // // check if we found the name or we need to add a new name // if (!pNameAddr) { // // create a new name for the domain list // if (pNameAddr = NbtAllocMem (sizeof(tNAMEADDR), NBT_TAG2('09'))) { CTEZeroMemory(pNameAddr,sizeof(tNAMEADDR)); pIpAddr = NbtAllocMem (INITIAL_DOM_SIZE, NBT_TAG2('10')); if (pIpAddr) { CTEMemCopy(pNameAddr->Name,pName,NETBIOS_NAME_SIZE); pNameAddr->pLmhSvcGroupList = pIpAddr; *pIpAddr++ = IpAddress; *pIpAddr = (ULONG)-1; pNameAddr->NameTypeState = NAMETYPE_INET_GROUP; pNameAddr->MaxDomainAddrLength = INITIAL_DOM_SIZE; pNameAddr->CurrentLength = 2*sizeof(ULONG); pNameAddr->Verify = REMOTE_NAME; NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE); InsertHeadList(pDomainHead,&pNameAddr->Linkage); } else { CTEMemFree(pNameAddr); pNameAddr = NULL; } } } if (pNameAddr && fPreload) { pNameAddr->fPreload = TRUE; } return(STATUS_SUCCESS); } //---------------------------------------------------------------------------- tNAMEADDR * FindInDomainList ( IN PUCHAR pName, IN PLIST_ENTRY pDomainHead ) /*++ Routine Description: This function finds a name in the domain list passed in. Arguments: name to find head of list to look on Return Value: ptr to pNameaddr --*/ { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; tNAMEADDR *pNameAddr; pHead = pEntry = pDomainHead; while ((pEntry = pEntry->Flink) != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); if (strncmp(pNameAddr->Name,pName,NETBIOS_NAME_SIZE) == 0) { return(pNameAddr); } } return(NULL); } //---------------------------------------------------------------------------- VOID MakeNewListCurrent ( PLIST_ENTRY pTmpDomainList ) /*++ Routine Description: This function frees the old entries on the DomainList and hooks up the new entries Arguments: pTmpDomainList - list entry to the head of a new domain list Return Value: --*/ { CTELockHandle OldIrq; tNAMEADDR *pNameAddr; PLIST_ENTRY pEntry; PLIST_ENTRY pHead; NTSTATUS status; CTESpinLock(&NbtConfig.JointLock,OldIrq); if (!IsListEmpty(pTmpDomainList)) { // // free the old list elements // pHead = &DomainNames.DomainList; pEntry = pHead->Flink; while (pEntry != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); pEntry = pEntry->Flink; RemoveEntryList(&pNameAddr->Linkage); // // initialize linkage so that if the nameaddr is being // referenced now, when it does get freed in a subsequent // call to NBT_DEREFERENCE_NAMEADDR it will not // remove it from any lists // InitializeListHead(&pNameAddr->Linkage); // // Since the name could be in use now we must dereference rather // than just free it outright // NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } // // See if any of the new names has to be preloaded! // pEntry = pTmpDomainList->Flink; while (pEntry != pTmpDomainList) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); pEntry = pEntry->Flink; if (pNameAddr->fPreload) { RemoveEntryList(&pNameAddr->Linkage); InitializeListHead(&pNameAddr->Linkage); status = AddToHashTable (NbtConfig.pRemoteHashTbl, pNameAddr->Name, NbtConfig.pScope, 0, 0, pNameAddr, &pNameAddr, NULL, NAME_RESOLVED_BY_LMH_P | NAME_ADD_INET_GROUP); if ((status == STATUS_SUCCESS) || ((status == STATUS_PENDING) && (!(pNameAddr->NameTypeState & PRELOADED)))) { // // this prevents the name from being deleted by the Hash Timeout code // NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_PRELOADED); pNameAddr->Ttl = 0xFFFFFFFF; pNameAddr->NameTypeState |= PRELOADED | STATE_RESOLVED; pNameAddr->NameTypeState &= ~STATE_CONFLICT; pNameAddr->AdapterMask = (CTEULONGLONG)-1; } } } DomainNames.DomainList.Flink = pTmpDomainList->Flink; DomainNames.DomainList.Blink = pTmpDomainList->Blink; pTmpDomainList->Flink->Blink = &DomainNames.DomainList; pTmpDomainList->Blink->Flink = &DomainNames.DomainList; } CTESpinFree(&NbtConfig.JointLock,OldIrq); } //---------------------------------------------------------------------------- NTSTATUS NtProcessLmHSvcIrp( IN tDEVICECONTEXT *pDeviceContext, IN PVOID *pBuffer, IN LONG Size, IN PCTE_IRP pIrp, IN enum eNbtLmhRequestType RequestType ) /*++ Routine Description: This function is used by LmHsvc Dll to collect requests for Pinging IP addresses or querying through DNS. The request is sent to LmhSvc in the buffer associated with this request. Arguments: Return Value: STATUS_PENDING if the buffer is to be held on to, the normal case. Notes: --*/ { NTSTATUS status; NTSTATUS Locstatus; CTELockHandle OldIrq; tIPADDR_BUFFER_DNS *pIpAddrBuf; PVOID pClientCompletion; PVOID pClientContext; tDGRAM_SEND_TRACKING *pTracker; ULONG IpAddrsList[MAX_IPADDRS_PER_HOST+1]; NBT_WORK_ITEM_CONTEXT *pContext; BOOLEAN CompletingAnotherQuery = FALSE; tLMHSVC_REQUESTS *pLmhRequest; tDEVICECONTEXT *pDeviceContextRequest; pIpAddrBuf = (tIPADDR_BUFFER_DNS *)pBuffer; switch (RequestType) { case NBT_PING_IP_ADDRS: { pLmhRequest = &CheckAddr; break; } case NBT_RESOLVE_WITH_DNS: { pLmhRequest = &DnsQueries; break; } default: { ASSERTMSG ("Nbt.NtProcessLmHSvcIrp: ERROR - Invalid Request from LmhSvc Dll\n", 0); return STATUS_UNSUCCESSFUL; } } CTESpinLock(&NbtConfig.JointLock,OldIrq); // // If we already have an Irp posted, return this Irp -- Bug # 311924 // if ((pLmhRequest->QueryIrp) && (!pLmhRequest->ResolvingNow)) { CTESpinFree (&NbtConfig.JointLock,OldIrq); KdPrint (("Nbt.NtProcessLmHSvcIrp: ERROR -- duplicate request Irp!\n")); NTIoComplete (pIrp, STATUS_OBJECT_PATH_INVALID, 0); NbtTrace(NBT_TRACE_NAMESRV, ("%!FUNC! duplicate Lmhosts request")); return STATUS_OBJECT_PATH_INVALID; } IoMarkIrpPending(pIrp); pLmhRequest->QueryIrp = pIrp; status = STATUS_PENDING; if (pLmhRequest->ResolvingNow) { // // if the client got tired of waiting for DNS, the NbtCancelWaitForLmhSvcIrp // in ntisol.c will have cleared the pContext value when cancelling the // irp, so check for that here. // if (pLmhRequest->Context) { pContext = (NBT_WORK_ITEM_CONTEXT *) pLmhRequest->Context; pLmhRequest->Context = NULL; pDeviceContextRequest = pContext->pDeviceContext; if (NBT_REFERENCE_DEVICE (pDeviceContextRequest, REF_DEV_LMH, TRUE)) { NbtCancelCancelRoutine (((tDGRAM_SEND_TRACKING *) (pContext->pClientContext))->pClientIrp); CTESpinFree(&NbtConfig.JointLock,OldIrq); ASSERT(sizeof(pIpAddrBuf->pwName) == DNS_NAME_BUFFER_LENGTH * sizeof(pIpAddrBuf->pwName[0])); pIpAddrBuf->pwName[DNS_NAME_BUFFER_LENGTH-1] = 0; NbtCompleteLmhSvcRequest (pContext, pIpAddrBuf->IpAddrsList, RequestType, pIpAddrBuf->NameLen, pIpAddrBuf->pwName, (BOOLEAN)pIpAddrBuf->Resolved); CTESpinLock(&NbtConfig.JointLock,OldIrq); NBT_DEREFERENCE_DEVICE (pDeviceContextRequest, REF_DEV_LMH, TRUE); } else { ASSERT (0); } } else { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NtProcessLmHSvcIrp[%s]: No Context!! *******\r\n", (RequestType == NBT_RESOLVE_WITH_DNS ? "NBT_RESOLVE_WITH_DNS" : "NBT_PING_IP_ADDRS"))); } pLmhRequest->ResolvingNow = FALSE; // // are there any more name query requests to process? // while (!IsListEmpty(&pLmhRequest->ToResolve)) { PLIST_ENTRY pEntry; pEntry = RemoveHeadList(&pLmhRequest->ToResolve); pContext = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); CTESpinFree(&NbtConfig.JointLock,OldIrq); Locstatus = NbtProcessLmhSvcRequest (pContext, RequestType); if (NT_SUCCESS(Locstatus)) { CTESpinLock(&NbtConfig.JointLock,OldIrq); CompletingAnotherQuery = TRUE; break; } // // if it failed then complete the irp now // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NtProcessLmHSvcIrp[%s]: NbtProcessLmhSvcRequest failed with %x\r\n", (RequestType==NBT_RESOLVE_WITH_DNS ? "NBT_RESOLVE_WITH_DNS":"NBT_PING_IP_ADDRS"), Locstatus)); pClientCompletion = pContext->ClientCompletion; pClientContext = pContext->pClientContext; pTracker = pContext->pTracker; // // Clear the Cancel Routine now // (VOID)NbtCancelCancelRoutine(((tDGRAM_SEND_TRACKING *)pClientContext)->pClientIrp); if (pTracker) { if (pTracker->pNameAddr) { SetNameState (pTracker->pNameAddr, NULL, FALSE); pTracker->pNameAddr = NULL; } // // pTracker is NULL for Ping requests, hence this dereference is // done only for Dns requests // NBT_DEREFERENCE_TRACKER(pTracker, FALSE); } CompleteClientReq(pClientCompletion, pClientContext, STATUS_BAD_NETWORK_PATH); CTEMemFree(pContext); CTESpinLock(&NbtConfig.JointLock,OldIrq); } } // // We are holding onto the Irp, so set the cancel routine. // (Since we may have released the lock earlier, we also need // to ensure that no other Query has completed the Irp!) // if ((!CompletingAnotherQuery) && (!pLmhRequest->ResolvingNow) && (pLmhRequest->QueryIrp == pIrp)) { status = NTCheckSetCancelRoutine(pIrp, NbtCancelLmhSvcIrp, pDeviceContext); if (!NT_SUCCESS(status)) { // the irp got cancelled so complete it now // pLmhRequest->QueryIrp = NULL; pLmhRequest->pIpAddrBuf = NULL; CTESpinFree(&NbtConfig.JointLock,OldIrq); NTIoComplete(pIrp,status,0); } else { CTESpinFree(&NbtConfig.JointLock,OldIrq); status = STATUS_PENDING; } } else { CTESpinFree(&NbtConfig.JointLock,OldIrq); } return(status); } //---------------------------------------------------------------------------- NTSTATUS NbtProcessLmhSvcRequest( IN NBT_WORK_ITEM_CONTEXT *pContext, IN enum eNbtLmhRequestType RequestType ) /*++ Routine Description: This function is called to pass a NBT request to ping IP addrs or query DNS to the LmhSvc Dll Arguments: Return Value: STATUS_PENDING if the buffer is to be held on to , the normal case. Notes: --*/ { NTSTATUS status = STATUS_SUCCESS; tIPADDR_BUFFER_DNS *pIpAddrBuf; PCTE_IRP pIrp; tDGRAM_SEND_TRACKING *pTracker; tDGRAM_SEND_TRACKING *pClientTracker; CTELockHandle OldIrq; PCHAR pDestName; ULONG NameLen, NumAddrs; tLMHSVC_REQUESTS *pLmhRequest; switch (RequestType) { case NBT_PING_IP_ADDRS: { pLmhRequest = &CheckAddr; break; } case NBT_RESOLVE_WITH_DNS: { pLmhRequest = &DnsQueries; break; } default: { ASSERTMSG ("Nbt.NbtProcessLmHSvcRequest: ERROR - Invalid Request type\n", 0); return STATUS_UNSUCCESSFUL; } } CTESpinLock(&NbtConfig.JointLock,OldIrq); pContext->TimedOut = FALSE; if ((!NBT_VERIFY_HANDLE (pContext->pDeviceContext, NBT_VERIFY_DEVCONTEXT)) || (!pLmhRequest->QueryIrp)) { // // Either the device is going away, or // the irp either never made it down here, or it was cancelled, // so pretend the name query timed out. // IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NbtProcessLmhSvcRequest[%s]: QueryIrp is NULL, returning\r\n", (RequestType == NBT_RESOLVE_WITH_DNS ? "NBT_RESOLVE_WITH_DNS" : "NBT_PING_IP_ADDRS"))); CTESpinFree(&NbtConfig.JointLock,OldIrq); if (pLmhRequest->QueryIrp) { NbtTrace(NBT_TRACE_NAMESRV, ("return STATUS_BAD_NETWORK_PATH because the device is going away")); } else { NbtTrace(NBT_TRACE_NAMESRV, ("LmHost services didn't start")); } return(STATUS_BAD_NETWORK_PATH); } else if (!pLmhRequest->ResolvingNow) { pIrp = pLmhRequest->QueryIrp; if ((!pLmhRequest->pIpAddrBuf) && (!(pLmhRequest->pIpAddrBuf = (tIPADDR_BUFFER_DNS *) MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority)))) { CTESpinFree(&NbtConfig.JointLock,OldIrq); NbtTrace(NBT_TRACE_NAMESRV, ("%!FUNC! returns STATUS_UNSUCCESSFUL")); return(STATUS_UNSUCCESSFUL); } pIpAddrBuf = pLmhRequest->pIpAddrBuf; pLmhRequest->ResolvingNow = TRUE; pLmhRequest->Context = pContext; pTracker = pContext->pTracker; // this is the name query tracker (for Dns queries only) pClientTracker = (tDGRAM_SEND_TRACKING *)pContext->pClientContext; // session setup tracker switch (RequestType) { case NBT_PING_IP_ADDRS: { ASSERT(pTracker == NULL); // // copy the IP addrs for lmhsvc to ping (upto MAX_IPADDRS_PER_HOST) ... // NumAddrs = pClientTracker->NumAddrs > MAX_IPADDRS_PER_HOST ? MAX_IPADDRS_PER_HOST : pClientTracker->NumAddrs; CTEMemCopy(pIpAddrBuf->IpAddrsList, pClientTracker->IpList, NumAddrs * sizeof(ULONG)); pIpAddrBuf->IpAddrsList[NumAddrs] = 0; break; } case NBT_RESOLVE_WITH_DNS: { WCHAR *UnicodeDestName; UnicodeDestName = pClientTracker? pClientTracker->UnicodeDestName: NULL; // // whenever dest. name is 16 bytes long (or smaller), we have no // way of knowing if its a netbios name or a dns name, so we presume // it's netbios name, go to wins, broadcast etc. and then come to dns // In this case, the name query tracker will be setup, so be non-null // if (pTracker) { pDestName = pTracker->pNameAddr->Name; NameLen = NETBIOS_NAME_SIZE; } // // if the dest name is longer than 16 bytes, it's got to be dns name so // we bypass wins etc. and come straight to dns. In this case, we didn't // set up a name query tracker so it will be null. Use the session setup // tracker (i.e. pClientTracker) to get the dest name // else { ASSERT(pClientTracker); pDestName = pClientTracker->pDestName; NameLen = pClientTracker->RemoteNameLength; } if ((NameLen == NETBIOS_NAME_SIZE) && (!(IsValidDnsNameTag (pDestName[NETBIOS_NAME_SIZE-1])))) { NbtTrace(NBT_TRACE_NAMESRV, ("%!FUNC! returns STATUS_BAD_NETWORK_PATH %02x", (unsigned)pDestName[NETBIOS_NAME_SIZE-1])); status = STATUS_BAD_NETWORK_PATH; } else { // // Ignore the 16th byte only if it is a non-DNS name character (we should be // safe below 0x20). This will allow queries to DNS names which are exactly 16 // characters long. // if (NameLen == NETBIOS_NAME_SIZE) { if ((pDestName[NETBIOS_NAME_SIZE-1] <= 0x20 ) || (pDestName[NETBIOS_NAME_SIZE-1] >= 0x7f )) { NameLen = NETBIOS_NAME_SIZE-1; // ignore 16th byte } } else if (NameLen > DNS_MAX_NAME_LENGTH) { NameLen = DNS_MAX_NAME_LENGTH; } // // copy the name to the Irps return buffer for lmhsvc to resolve with // a gethostbyname call // if (UnicodeDestName) { int len; len = pClientTracker->UnicodeRemoteNameLength; if (len > sizeof(pIpAddrBuf->pwName - sizeof(WCHAR))) { len = sizeof(pIpAddrBuf->pwName) - sizeof(WCHAR); } ASSERT((len % sizeof(WCHAR)) == 0); CTEMemCopy(pIpAddrBuf->pwName, UnicodeDestName, len); pIpAddrBuf->pwName[len/sizeof(WCHAR)] = 0; pIpAddrBuf->NameLen = len; pIpAddrBuf->bUnicode = TRUE; } else { // // I would like to maintain only UNICODE interface between NetBT and LmhSVC. // But I cannot do RtlAnsiStringToUnicodeString here due to IRQ level here. // pIpAddrBuf->bUnicode = FALSE; CTEMemCopy(pIpAddrBuf->pName, pDestName, NameLen); pIpAddrBuf->pName[NameLen] = 0; pIpAddrBuf->NameLen = NameLen; } } break; } default: { // // This code path should never be hit! // ASSERT(0); } } // switch // // Since datagrams are buffered there is no client irp to get cancelled // since the client's irp is returned immediately -so this check // is only for connections being setup or QueryFindname or // nodestatus, where we allow the irp to // be cancelled. // if ((NT_SUCCESS(status)) && (pClientTracker->pClientIrp)) { // // allow the client to cancel the name query Irp - no need to check // if the client irp was already cancelled or not since the DNS query // will complete and find no client request and stop. // status = NTCheckSetCancelRoutine(pClientTracker->pClientIrp, NbtCancelWaitForLmhSvcIrp,NULL); } // // pass the irp up to lmhsvc.dll to do a gethostbyname call to // sockets // The Irp will return to NtDnsNameResolve, above // if (NT_SUCCESS(status)) { pLmhRequest->pIpAddrBuf = NULL; CTESpinFree(&NbtConfig.JointLock,OldIrq); NTIoComplete(pLmhRequest->QueryIrp,STATUS_SUCCESS,0); return (STATUS_PENDING); } // // We failed to set the cancel routine, so undo setting up the // the pLmhRequest structure. // NbtTrace(NBT_TRACE_NAMESRV, ("%!FUNC! returns %!status!", status)); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NbtProcessLmhSvcRequest[%s]: CheckSet (submitting) failed with %x\r\n", (RequestType == NBT_RESOLVE_WITH_DNS ? "NBT_RESOLVE_WITH_DNS" : "NBT_PING_IP_ADDRS"),status)); pLmhRequest->ResolvingNow = FALSE; pLmhRequest->Context = NULL; CTESpinFree(&NbtConfig.JointLock,OldIrq); } else { pClientTracker = (tDGRAM_SEND_TRACKING *)pContext->pClientContext; // // Since datagrams are buffered there is no client irp to get cancelled // since the client's irp is returned immediately -so this check // is only for connections being setup, where we allow the irp to // be cancelled. // // // allow the client to cancel the name query Irp // if (pClientTracker->pClientIrp) // check if this is the session setup tracker { status = NTCheckSetCancelRoutine(pClientTracker->pClientIrp, NbtCancelWaitForLmhSvcIrp,NULL); } if (NT_SUCCESS(status)) { // the irp is busy resolving another name, so wait for it to return // down here again, mean while, Queue the name query // InsertTailList(&pLmhRequest->ToResolve, &pContext->Item.List); } else { IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NbtProcessLmhSvcRequest[%s]: CheckSet (queuing) failed with %x\r\n", (RequestType == NBT_RESOLVE_WITH_DNS ? "NBT_RESOLVE_WITH_DNS" : "NBT_PING_IP_ADDRS"),status)); NbtTrace(NBT_TRACE_NAMESRV, ("%!FUNC! returns %!status!", status)); } CTESpinFree(&NbtConfig.JointLock,OldIrq); } if (NT_SUCCESS(status)) { status = STATUS_PENDING; } return(status); } //---------------------------------------------------------------------------- extern VOID SetNameState( IN tNAMEADDR *pNameAddr, IN PULONG pIpList, IN BOOLEAN IpAddrResolved ) /*++ Routine Description: This function dereferences the pNameAddr and sets the state to Released just incase the dereference does not delete the entry right away, due to another outstanding reference against the name. Arguments: Context - Return Value: none --*/ { CTELockHandle OldIrq; CTESpinLock(&NbtConfig.JointLock,OldIrq); if (IpAddrResolved) { pNameAddr->IpAddress = pIpList[0]; } else { pNameAddr->NameTypeState &= ~NAME_STATE_MASK; pNameAddr->NameTypeState |= STATE_RELEASED; pNameAddr->pTracker = NULL; } ASSERT (pNameAddr->RefCount == 1); NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_QUERY_ON_NET, TRUE); CTESpinFree(&NbtConfig.JointLock,OldIrq); } //---------------------------------------------------------------------------- VOID NbtCompleteLmhSvcRequest( IN NBT_WORK_ITEM_CONTEXT *Context, IN ULONG *IpList, IN enum eNbtLmhRequestType RequestType, IN ULONG lNameLength, IN PWSTR pwsName, // The rosolved name return by LmhSvc IN BOOLEAN IpAddrResolved ) /*++ Routine Description: If the destination name is of the form 11.101.4.25 or is a dns name (i.e. of the form ftp.microsoft.com) then we come to this function. In addition to doing some house keeping, if the name did resolve then we also send out a nodestatus request to find out the server name for that ipaddr Arguments: Context - (NBT_WORK_ITEM_CONTEXT) IpList - Array of ipaddrs if resolved (i.e. IpAddrResolved is TRUE) IpAddrResolved - TRUE if ipaddr could be resolved, FALSE otherwise Return Value: Nothing Notes: --*/ { NTSTATUS status; PVOID pClientCompletion; tDGRAM_SEND_TRACKING *pTracker; tDGRAM_SEND_TRACKING *pClientTracker; ULONG TdiAddressType = TDI_ADDRESS_TYPE_NETBIOS; ULONG IpAddrsList[MAX_IPADDRS_PER_HOST+1]; tDEVICECONTEXT *pDeviceContext; int i; tCONNECTELE *pConnEle; CTEPagedCode(); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NbtCompleteLmhSvcRequest: Entered ...\n")); pTracker = Context->pTracker; pClientCompletion = Context->ClientCompletion; pClientTracker = (tDGRAM_SEND_TRACKING *) Context->pClientContext; pDeviceContext = pClientTracker->pDeviceContext; // whether or not name resolved, we don't need this nameaddr anymore // (if name resolved, then we do a node status to that addr and create // a new nameaddr for the server name in ExtractServerName) // pTracker is null if we went straight to dns (without wins etc) if (pTracker) { // // Set some info in case some client is still resolving the name // SetNameState (pTracker->pNameAddr, IpList, IpAddrResolved); pTracker->pNameAddr = NULL; } (VOID)NbtCancelCancelRoutine (pClientTracker->pClientIrp); pClientTracker->pTrackerWorker = NULL; // The original NameQuery Tracker will be dereferenced below status = STATUS_BAD_NETWORK_PATH; if (RequestType == NBT_RESOLVE_WITH_DNS) { TdiAddressType = ((pTracker == NULL) ? pClientTracker->AddressType: TDI_ADDRESS_TYPE_NETBIOS); } // // If we failed to resolve it, set the state approriately! // if (!IpAddrResolved) { if ((TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS_EX) && (pConnEle = pClientTracker->pConnEle)) // NULL if request was to send Datagram! { pConnEle->RemoteNameDoesNotExistInDNS = TRUE; } } else if (NBT_VERIFY_HANDLE(pDeviceContext, NBT_VERIFY_DEVCONTEXT)) // check if this Device is still up! { // the name was resolved successfully! switch (RequestType) { case NBT_RESOLVE_WITH_DNS: { // bug #20697, #95241 if (pwsName && pClientTracker->pNetbiosUnicodeEX && (pClientTracker->pNetbiosUnicodeEX->NameBufferType == NBT_READWRITE || pClientTracker->pNetbiosUnicodeEX->NameBufferType == NBT_WRITEONLY)) { UNICODE_STRING temp; temp = pClientTracker->pNetbiosUnicodeEX->RemoteName; // // Has the buffer changed? // if (memcmp(&temp, &pClientTracker->ucRemoteName, sizeof(UNICODE_STRING)) == 0) { ASSERT(lNameLength <= (DNS_NAME_BUFFER_LENGTH-1) * sizeof(pwsName[0])); ASSERT((lNameLength%sizeof(WCHAR)) == 0); // // Make sure we don't overrun the buffer // if (lNameLength > temp.MaximumLength - sizeof(WCHAR)) { // Don't return STATUS_BUFFER_OVERFLOW since it is just a warning instead of error status = STATUS_BUFFER_TOO_SMALL; break; } CTEMemCopy(temp.Buffer, pwsName, lNameLength); temp.Buffer[lNameLength/sizeof(WCHAR)] = 0; temp.Length = (USHORT)lNameLength; pClientTracker->pNetbiosUnicodeEX->NameBufferType = NBT_WRITTEN; pClientTracker->pNetbiosUnicodeEX->RemoteName = temp; IF_DBG(NBT_DEBUG_NETBIOS_EX) KdPrint(("netbt!NbtCompleteLmhSvcRequest: Update Unicode Name at %d of %s\n" "\t\tDNS return (%ws)\n", __LINE__, __FILE__, pwsName)); } } if ((TdiAddressType == TDI_ADDRESS_TYPE_NETBIOS) && (!IsDeviceNetbiosless(pDeviceContext))) // Can't do a NodeStatus on the SMB port { for (i=0; iFlags |= NBT_DNS_SERVER; // Set this so that the completion will know pClientTracker->CompletionRoutine = pClientCompletion; status = NbtSendNodeStatus(pDeviceContext, NULL, IpAddrsList, pClientTracker, ExtractServerNameCompletion); // // If we succeeded in sending a Node status, exit now, // without calling the completion routine // if (NT_SUCCESS(status)) { // pTracker is null if we went straight to dns (without wins etc) or // if this was a Ping request if (pTracker) { NBT_DEREFERENCE_TRACKER(pTracker, FALSE); } CTEMemFree(Context); return; } break; } // // The Address is of type TDI_ADDRESS_TYPE_NETBIOS_EX, // so now handle this scenario in the same way as for // for a Ping request! // // NO break! } case NBT_PING_IP_ADDRS: { // // add this server name to the remote hashtable // Call into IP to determine the outgoing interface for this address // pDeviceContext = GetDeviceFromInterface (htonl(IpList[0]), TRUE); status = LockAndAddToHashTable(NbtConfig.pRemoteHashTbl, pClientTracker->pDestName, NbtConfig.pScope, IpList[0], NBT_UNIQUE, NULL, NULL, pDeviceContext, (USHORT) ((RequestType == NBT_RESOLVE_WITH_DNS) ? NAME_RESOLVED_BY_DNS : NAME_RESOLVED_BY_WINS | NAME_RESOLVED_BY_BCAST)); if (pDeviceContext) { NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_OUT_FROM_IP, FALSE); } // // STATUS_PENDING will be returned if the name already existed // in the hashtable // if (status == STATUS_PENDING) { status = STATUS_SUCCESS; } IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.NbtCompleteLmhSvcRequest: AddRecordToHashTable Status %lx\n",status)); break; } default: { ASSERT(0); } } // switch } // pTracker is null if we went straight to dns (without wins etc) or // if this was a Ping request if (pTracker) { NBT_DEREFERENCE_TRACKER(pTracker, FALSE); } NbtTrace(NBT_TRACE_NAMESRV, ("%!FUNC! complete client request with %!status!", status)); CompleteClientReq(pClientCompletion, pClientTracker, status); CTEMemFree(Context); } #endif // !VXD //---------------------------------------------------------------------------- NTSTATUS PreloadEntry( IN PUCHAR name, IN tIPADDRESS inaddr ) /*++ Routine Description: This function adds an lmhosts entry to nbt's name cache. For each lmhosts entry, NSUFFIXES unique cache entries are created. Even when some cache entries can't be created, this function doesn't attempt to remove any that were successfully added to the cache. Arguments: name - the unencoded NetBIOS name specified in lmhosts inaddr - the ip address, in host byte order Return Value: The number of new name cache entries created. --*/ { NTSTATUS status; tNAMEADDR *pNameAddr; LONG nentries; LONG Len; CHAR temp[NETBIOS_NAME_SIZE+1]; CTELockHandle OldIrq; LONG NumberToAdd; tDEVICECONTEXT *pDeviceContext; // if all 16 bytes are present then only add that name exactly as it // is. // Len = strlen(name); // // if this string is exactly 16 characters long, do not expand // into 0x00, 0x03,0x20 names. Just add the single name as it is. // if (Len == NETBIOS_NAME_SIZE) { NumberToAdd = 1; } else { NumberToAdd = NSUFFIXES; } for (nentries = 0; nentries < NumberToAdd; nentries++) { // for names less than 16 bytes, expand out to 16 and put a 16th byte // on according to the suffix array // if (Len != NETBIOS_NAME_SIZE) { LmExpandName(temp, name, Suffix[nentries]); } else { CTEMemCopy(temp,name,NETBIOS_NAME_SIZE); } pDeviceContext = GetDeviceFromInterface (htonl(inaddr), TRUE); CTESpinLock(&NbtConfig.JointLock,OldIrq); status = AddToHashTable (NbtConfig.pRemoteHashTbl, temp, NbtConfig.pScope, inaddr, NBT_UNIQUE, NULL, &pNameAddr, pDeviceContext, NAME_RESOLVED_BY_LMH_P); // if the name is already in the hash table, the status code is // status pending. This could happen if the preloads are purged // when one is still being referenced by another part of the code, // and was therefore not deleted. We do not want to add the name // twice, so we just change the ip address to agree with the preload // value // if ((status == STATUS_SUCCESS) || ((status == STATUS_PENDING) && (!(pNameAddr->NameTypeState & PRELOADED)))) { // // this prevents the name from being deleted by the Hash Timeout code // pNameAddr->NameTypeState |= PRELOADED | STATE_RESOLVED; pNameAddr->NameTypeState &= ~STATE_CONFLICT; pNameAddr->Ttl = 0xFFFFFFFF; pNameAddr->Verify = REMOTE_NAME; NBT_REFERENCE_NAMEADDR (pNameAddr, REF_NAME_PRELOADED); if (pDeviceContext) { pNameAddr->AdapterMask |= pDeviceContext->AdapterMask; } } else if (status == STATUS_PENDING) { pNameAddr->IpAddress = inaddr; } if (pDeviceContext) { NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_OUT_FROM_IP, TRUE); } CTESpinFree(&NbtConfig.JointLock,OldIrq); } return(STATUS_SUCCESS); } // PreloadEntry //---------------------------------------------------------------------------- extern VOID RemovePreloads ( ) /*++ Routine Description: This function removes preloaded entries from the remote hash table. If it finds any of the preloaded entries are active with a ref count above the base level of 2, then it returns true. Arguments: none Return Value: none --*/ { tNAMEADDR *pNameAddr; PLIST_ENTRY pHead,pEntry; CTELockHandle OldIrq; tHASHTABLE *pHashTable; BOOLEAN FoundActivePreload=FALSE; LONG i; // // go through the remote table deleting names that have the PRELOAD // bit set. // pHashTable = NbtConfig.pRemoteHashTbl; CTESpinLock(&NbtConfig.JointLock,OldIrq); for (i=0;i < pHashTable->lNumBuckets ;i++ ) { pHead = &pHashTable->Bucket[i]; pEntry = pHead->Flink; while (pEntry != pHead) { pNameAddr = CONTAINING_RECORD(pEntry,tNAMEADDR,Linkage); pEntry = pEntry->Flink; // // Delete preloaded entries that are not in use by some other // part of the code now. Note that preloaded entries start with // a ref count of 2 so that the normal remote hashtimeout code // will not delete them // if ((pNameAddr->NameTypeState & PRELOADED) && (pNameAddr->RefCount == 2)) { NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_PRELOADED, TRUE); NBT_DEREFERENCE_NAMEADDR (pNameAddr, REF_NAME_REMOTE, TRUE); } } } CTESpinFree(&NbtConfig.JointLock,OldIrq); return; } //---------------------------------------------------------------------------- LONG PrimeCache( IN PUCHAR path, IN PUCHAR ignored, IN CHAR RecurseDepth, OUT BOOLEAN *ignored2 ) /*++ Routine Description: This function is called to prime the cache with entries in the lmhosts file that are marked as preload entries. Arguments: path - a fully specified path to a lmhosts file ignored - unused RecurseDepth- the depth to which we can resurse -- 0 => no more recursion Return Value: Number of new cache entries that were added, or -1 if there was an i/o error. --*/ { int nentries; PUCHAR buffer; PLM_FILE pfile; NTSTATUS status; int count, nwords; unsigned long temp; INCLUDE_STATE incstate; PUCHAR token[MaxTokens]; ULONG inaddr; LINE_CHARACTERISTICS current; UCHAR Name[NETBIOS_NAME_SIZE+1]; ULONG IpAddr; LIST_ENTRY TmpDomainList; int domtoklen; CTEPagedCode(); if (!NbtConfig.EnableLmHosts) { return(STATUS_SUCCESS); } InitializeListHead(&TmpDomainList); // // Check for infinitely recursive name lookup in a #INCLUDE. // if (LmpBreakRecursion(path, "", 1) == TRUE) { return (-1); } pfile = LmOpenFile(path); if (!pfile) { return(-1); } nentries = 0; incstate = MustInclude; domtoklen = strlen(DOMAIN_TOKEN); while (buffer = LmFgets(pfile, &count)) { #ifndef VXD if ((NbtConfig.MaxPreloadEntries - nentries) < 3) { break; } #else if ( nentries >= (NbtConfig.MaxPreloadEntries - 3) ) { break; } #endif nwords = MaxTokens; current = LmpGetTokens(buffer, token, &nwords); // if there is and error or no name on the line, then continue // to the next line. // if (current.l_category == ErrorLine) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.PrimeCache: Error line in Lmhost file\n")); continue; } if (current.l_category != BeginAlternate && current.l_category != EndAlternate) { if (token[NbName] == NULL) { IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.PrimeCache: Error line in Lmhost file\n")); continue; } } if (current.l_preload) { status = ConvertDottedDecimalToUlong(token[IpAddress],&inaddr); if (NT_SUCCESS(status)) { status = PreloadEntry (token[NbName], inaddr); if (NT_SUCCESS(status)) { nentries++; } } } switch ((ULONG)current.l_category) { case Domain: if ((nwords - 1) < GroupName) { continue; } // // and add '1C' on the end // LmExpandName(Name, token[GroupName]+ domtoklen, SPECIAL_GROUP_SUFFIX); status = ConvertDottedDecimalToUlong(token[IpAddress],&IpAddr); if (NT_SUCCESS(status)) { AddToDomainList (Name, IpAddr, &TmpDomainList, (BOOLEAN)current.l_preload); } continue; case Include: if (!RecurseDepth || ((incstate == SkipInclude) || (nwords < 2))) { continue; } #ifdef VXD // // the buffer which we read into is reused for the next file: we // need the contents when we get back: back it up! // if we can't allocate memory, just skip this include // if ( !BackupCurrentData(pfile) ) { continue; } #endif temp = LmInclude(token[1], PrimeCache, NULL, (CHAR) (RecurseDepth-1), NULL); #ifdef VXD // // going back to previous file: restore the backed up data // RestoreOldData(pfile); #endif if (temp != -1) { if (incstate == TryToInclude) { incstate = SkipInclude; } nentries += temp; continue; } continue; case BeginAlternate: ASSERT(nwords == 1); incstate = TryToInclude; continue; case EndAlternate: ASSERT(nwords == 1); incstate = MustInclude; continue; default: continue; } } status = LmCloseFile(pfile); ASSERT(status == STATUS_SUCCESS); // // make this the new domain list // MakeNewListCurrent(&TmpDomainList); ASSERT(nentries >= 0); return(nentries); } // LmPrimeCache //---------------------------------------------------------------------------- extern VOID GetContext( IN OUT NBT_WORK_ITEM_CONTEXT **ppContext ) /*++ Routine Description: This function is called to get the context value to check if a name query has been cancelled or not. Arguments: Context - Return Value: none --*/ { CTELockHandle OldIrq; NBT_WORK_ITEM_CONTEXT *pContext; // // remove the Context value and return it. // CTESpinLock(&NbtConfig.JointLock,OldIrq); if (pContext = LmHostQueries.Context) { if ((*ppContext) && (*ppContext != pContext)) { pContext = NULL; } #ifndef VXD else if (NbtCancelCancelRoutine(((tDGRAM_SEND_TRACKING *)(pContext->pClientContext))->pClientIrp) == STATUS_CANCELLED) { pContext = NULL; } else #endif // VXD { LmHostQueries.Context = NULL; } } *ppContext = pContext; CTESpinFree(&NbtConfig.JointLock,OldIrq); } //---------------------------------------------------------------------------- extern NTSTATUS ChangeStateOfName ( IN tIPADDRESS IpAddress, IN NBT_WORK_ITEM_CONTEXT *pContext, IN OUT NBT_WORK_ITEM_CONTEXT **ppContext, IN USHORT NameAddFlags ) /*++ Routine Description: This function changes the state of a name and nulls the Context value in lmhostqueries. Arguments: pContext - The Context value if it has been removed from the LmHostQueries.Context ptr. ppContext - The Context we are processing Return Value: none --*/ { NTSTATUS status; CTELockHandle OldIrq; tDEVICECONTEXT *pDeviceContext; pDeviceContext = GetDeviceFromInterface(htonl(IpAddress), TRUE); if (pContext == NULL) { // // See if the name query is still active // pContext = *ppContext; GetContext (&pContext); } if (pContext) { // convert broadcast addresses to zero since NBT interprets zero // to be broadcast // if (IpAddress == (ULONG)-1) { IpAddress = 0; } CTESpinLock(&NbtConfig.JointLock,OldIrq); status = AddToHashTable (NbtConfig.pRemoteHashTbl, pContext->pTracker->pNameAddr->Name, NbtConfig.pScope, IpAddress, NBT_UNIQUE, NULL, NULL, pDeviceContext, NameAddFlags); // // this will free the pNameAddr, so do not access this after this point // NBT_DEREFERENCE_NAMEADDR (pContext->pTracker->pNameAddr, REF_NAME_QUERY_ON_NET, TRUE); pContext->pTracker->pNameAddr = NULL; CTESpinFree(&NbtConfig.JointLock,OldIrq); *ppContext = pContext; } else { *ppContext = NULL; } if (pDeviceContext) { NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_OUT_FROM_IP, FALSE); } return (STATUS_SUCCESS); } //---------------------------------------------------------------------------- VOID RemoveLmHRequests( IN tLMHSVC_REQUESTS *pLmHRequest, IN PLIST_ENTRY pTmpHead, IN tTIMERQENTRY *pTimerQEntry, IN tDEVICECONTEXT *pDeviceContext ) /*++ Routine Description: This routine is called to find timed out entries in the queue of lmhost or dns name queries. Arguments: Return Value: The function value is the status of the operation. --*/ { PLIST_ENTRY pEntry; NBT_WORK_ITEM_CONTEXT *pWiContext; BOOLEAN fRestartTimer = FALSE; // // check the currently processing LMHOSTS entry // if (pLmHRequest->Context) { pWiContext = (NBT_WORK_ITEM_CONTEXT *) pLmHRequest->Context; if ((pWiContext->TimedOut) || (pWiContext->pDeviceContext == pDeviceContext)) { pLmHRequest->Context = NULL; InsertTailList(pTmpHead, &pWiContext->Item.List); #ifndef VXD // Not for win95, MohsinA, 05-Dec-96. NbtCancelCancelRoutine(((tDGRAM_SEND_TRACKING *) (pWiContext->pClientContext))->pClientIrp); #endif } else { // // restart the timer // fRestartTimer = TRUE; pWiContext->TimedOut = TRUE; } } // // Check the list of queued entries // if (!IsListEmpty(&pLmHRequest->ToResolve)) { // // restart the timer // fRestartTimer = TRUE; pEntry = pLmHRequest->ToResolve.Flink; while (pEntry != &pLmHRequest->ToResolve) { pWiContext = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); pEntry = pEntry->Flink; if ((pWiContext->TimedOut) || (pWiContext->pDeviceContext == pDeviceContext)) { // // save on a temporary list and complete below // RemoveEntryList(&pWiContext->Item.List); InsertTailList(pTmpHead, &pWiContext->Item.List); } else { pWiContext->TimedOut = TRUE; } } } if ((fRestartTimer) && (pTimerQEntry)) { pTimerQEntry->Flags |= TIMER_RESTART; } } //---------------------------------------------------------------------------- VOID TimeoutLmHRequests( IN tTIMERQENTRY *pTimerQEntry, IN tDEVICECONTEXT *pDeviceContext, IN BOOLEAN fLocked, IN CTELockHandle *pJointLockOldIrq ) { PLIST_ENTRY pHead; PLIST_ENTRY pEntry; NBT_WORK_ITEM_CONTEXT *pWiContext; LIST_ENTRY TmpHead; InitializeListHead(&TmpHead); if (!fLocked) { CTESpinLock(&NbtConfig.JointLock,*pJointLockOldIrq); } // // check the currently processing LMHOSTS entry // RemoveLmHRequests (&LmHostQueries, &TmpHead, pTimerQEntry, pDeviceContext); RemoveLmHRequests (&CheckAddr, &TmpHead, pTimerQEntry, pDeviceContext); #ifndef VXD RemoveLmHRequests (&DnsQueries, &TmpHead, pTimerQEntry, pDeviceContext); #endif CTESpinFree(&NbtConfig.JointLock,*pJointLockOldIrq); if (!IsListEmpty(&TmpHead)) { pHead = &TmpHead; pEntry = pHead->Flink; while (pEntry != pHead) { pWiContext = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); pEntry = pEntry->Flink; RemoveEntryList(&pWiContext->Item.List); IF_DBG(NBT_DEBUG_LMHOST) KdPrint(("Nbt.TimeoutLmHRequests: Context=<%p>, pDeviceContext=<%p>\n", pWiContext, pDeviceContext)); RemoveNameAndCompleteReq(pWiContext,STATUS_TIMEOUT); } } if (fLocked) { CTESpinLock(&NbtConfig.JointLock,*pJointLockOldIrq); } } //---------------------------------------------------------------------------- VOID LmHostTimeout( PVOID pContext, PVOID pContext2, tTIMERQENTRY *pTimerQEntry ) /*++ Routine Description: This routine is called by the timer code when the timer expires. It marks all items in Lmhosts/Dns q as timed out and completes any that have already timed out with status timeout. Arguments: Return Value: The function value is the status of the operation. --*/ { CTELockHandle OldIrq; // // If the timer is NULL, it means that the Timer is currently // being stopped (usually at Unload time), so don't do anything! // if (!pTimerQEntry) { LmHostQueries.pTimer = NULL; return; } TimeoutLmHRequests (pTimerQEntry, NULL, FALSE, &OldIrq); // null the timer if we are not going to restart it. // if (!(pTimerQEntry->Flags & TIMER_RESTART)) { LmHostQueries.pTimer = NULL; } } //---------------------------------------------------------------------------- extern VOID StartLmHostTimer( IN NBT_WORK_ITEM_CONTEXT *pContext, IN BOOLEAN fLockedOnEntry ) /*++ Routine Description This routine handles setting up a timer to time the Lmhost entry. The Joint Spin Lock may be held when this routine is called Arguments: Return Values: VOID --*/ { NTSTATUS status; tTIMERQENTRY *pTimerEntry; CTELockHandle OldIrq; if (!fLockedOnEntry) { CTESpinLock(&NbtConfig.JointLock,OldIrq); } pContext->TimedOut = FALSE; // // start the timer if it is not running // if (!LmHostQueries.pTimer) { status = StartTimer(LmHostTimeout, NbtConfig.LmHostsTimeout, NULL, // context value NULL, // context2 value NULL, NULL, NULL, &pTimerEntry, 0, TRUE); IF_DBG(NBT_DEBUG_NAMESRV) KdPrint(("Nbt.StartLmHostTimer: Start Timer to time Lmhost Qing for pContext= %x,\n", pContext)); if (NT_SUCCESS(status)) { LmHostQueries.pTimer = pTimerEntry; } else { // we failed to get a timer, but that is not // then end of the world. The lmhost query will just // not timeout in 30 seconds. It may take longer if // it tries to include a remove file on a dead machine. // LmHostQueries.pTimer = NULL; } } if (!fLockedOnEntry) { CTESpinFree(&NbtConfig.JointLock,OldIrq); } } //---------------------------------------------------------------------------- NTSTATUS LmHostQueueRequest( IN tDGRAM_SEND_TRACKING *pTracker, IN PVOID pClientContext, IN PVOID ClientCompletion, IN PVOID pDeviceContext ) /*++ Routine Description: This routine exists so that LmHost requests will not take up more than one executive worker thread. If a thread is busy performing an Lmhost request, new requests are queued otherwise we could run out of worker threads and lock up the system. The Joint Spin Lock is held when this routine is called Arguments: pTracker - the tracker block for context DelayedWorkerRoutine - the routine for the Workerthread to call pDeviceContext - dev context that initiated this Return Value: --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; NBT_WORK_ITEM_CONTEXT *pContext; tDGRAM_SEND_TRACKING *pTrackClient; PCTE_IRP pIrp; BOOLEAN OnList; if (pContext = (NBT_WORK_ITEM_CONTEXT *)NbtAllocMem(sizeof(NBT_WORK_ITEM_CONTEXT),NBT_TAG('V'))) { pContext->pTracker = pTracker; pContext->pClientContext = pClientContext; pContext->ClientCompletion = ClientCompletion; pContext->pDeviceContext = pDeviceContext; pContext->TimedOut = FALSE; if (LmHostQueries.ResolvingNow) { // Lmhosts is busy resolving another name, so wait for it to return // mean while, Queue the name query // InsertTailList(&LmHostQueries.ToResolve,&pContext->Item.List); OnList = TRUE; } else { LmHostQueries.Context = pContext; LmHostQueries.ResolvingNow = TRUE; OnList = FALSE; if (!NT_SUCCESS (CTEQueueForNonDispProcessing (DelayedScanLmHostFile, pTracker, pClientContext, ClientCompletion, pDeviceContext, TRUE))) { LmHostQueries.Context = NULL; LmHostQueries.ResolvingNow = FALSE; CTEMemFree(pContext); return (STATUS_UNSUCCESSFUL); } } // // To prevent this name query from languishing on the Lmhost Q when // a #include on a dead machine is trying to be openned, start the // connection setup timer // StartLmHostTimer(pContext, TRUE); // // this is the session setup tracker // #ifndef VXD pTrackClient = (tDGRAM_SEND_TRACKING *)pClientContext; if (pIrp = pTrackClient->pClientIrp) { // // allow the client to cancel the name query Irp // // but do not call NTSetCancel... since it takes need to run // at non DPC level, and it calls the completion routine // which takes the JointLock that we already have. // status = NTCheckSetCancelRoutine(pTrackClient->pClientIrp, NbtCancelWaitForLmhSvcIrp,NULL); if (status == STATUS_CANCELLED) { // // since the name query is cancelled do not let lmhost processing // handle it. // if (OnList) { RemoveEntryList(&pContext->Item.List); } else { // // do not set resolving now to False since the work item // has been queued to the worker thread // LmHostQueries.Context = NULL; LmHostQueries.ResolvingNow = FALSE; } CTEMemFree(pContext); } return(status); } #endif status = STATUS_SUCCESS; } return(status); } //---------------------------------------------------------------------------- extern NBT_WORK_ITEM_CONTEXT * GetNameToFind( OUT PUCHAR pName ) /*++ Routine Description: This function is called to get the name to query from the LmHostQueries list. Arguments: Context - Return Value: none --*/ { tDGRAM_SEND_TRACKING *pTracker; CTELockHandle OldIrq; NBT_WORK_ITEM_CONTEXT *Context; PLIST_ENTRY pEntry; CTESpinLock(&NbtConfig.JointLock,OldIrq); // if the context value has been cleared then that name query has been // cancelled, so check for another one. // if (!(Context = LmHostQueries.Context)) { // // the current name query got canceled so see if there are any more // to service // if (!IsListEmpty(&LmHostQueries.ToResolve)) { pEntry = RemoveHeadList(&LmHostQueries.ToResolve); Context = CONTAINING_RECORD(pEntry,NBT_WORK_ITEM_CONTEXT,Item.List); LmHostQueries.Context = Context; } else { // // no more names to resolve, so clear the flag // LmHostQueries.ResolvingNow = FALSE; CTESpinFree(&NbtConfig.JointLock,OldIrq); return(NULL); } } pTracker = ((NBT_WORK_ITEM_CONTEXT *)Context)->pTracker; CTEMemCopy(pName,pTracker->pNameAddr->Name,NETBIOS_NAME_SIZE); CTESpinFree(&NbtConfig.JointLock,OldIrq); return(Context); } //---------------------------------------------------------------------------- extern VOID RemoveNameAndCompleteReq ( IN NBT_WORK_ITEM_CONTEXT *pContext, IN NTSTATUS status ) /*++ Routine Description: This function removes the name, cleans up the tracker and then completes the clients request. Arguments: Context - Return Value: none --*/ { tDGRAM_SEND_TRACKING *pTracker; PVOID pClientContext; PVOID pClientCompletion; CTELockHandle OldIrq; // if pContext is null the name query was cancelled during the // time it took to go read the lmhosts file, so don't do this // stuff // if (pContext) { pTracker = pContext->pTracker; pClientCompletion = pContext->ClientCompletion; pClientContext = pContext->pClientContext; CTEMemFree(pContext); #ifndef VXD // // clear out the cancel routine if there is an irp involved // CTESpinLock(&NbtConfig.JointLock,OldIrq); NbtCancelCancelRoutine( ((tDGRAM_SEND_TRACKING *)(pClientContext))->pClientIrp ); CTESpinFree(&NbtConfig.JointLock,OldIrq); #endif // remove the name from the hash table, since it did not resolve if (pTracker) { if ((status != STATUS_SUCCESS) && (pTracker->pNameAddr)) { SetNameState (pTracker->pNameAddr, NULL, FALSE); pTracker->pNameAddr = NULL; } // free the tracker and call the completion routine. // NBT_DEREFERENCE_TRACKER(pTracker, FALSE); } if (pClientCompletion) { CompleteClientReq(pClientCompletion, pClientContext, status); } } } //---------------------------------------------------------------------------- // // Alternative to the c-runtime // #ifndef VXD PCHAR Nbtstrcat( PUCHAR pch, PUCHAR pCat, LONG Len ) { STRING StringIn; STRING StringOut; RtlInitAnsiString(&StringIn, pCat); RtlInitAnsiString(&StringOut, pch); StringOut.MaximumLength = (USHORT)Len; // // increment to include the null on the end of the string since // we want that on the end of the final product // StringIn.Length++; RtlAppendStringToString(&StringOut,&StringIn); return(pch); } #else #define Nbtstrcat( a,b,c ) strcat( a,b ) #endif