mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3847 lines
106 KiB
3847 lines
106 KiB
/*++
|
|
|
|
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 <ctype.h>
|
|
#include <string.h>
|
|
|
|
#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
|
|
// --------------------------------------------------------------------------
|
|
// <computername> 01 Unique Messenger Service
|
|
// <\\--__MSBROWSE__> 01 Group Master Browser
|
|
// <domain> 1B Unique Domain Master Browser
|
|
// <domain> 1C Group Domain Controllers
|
|
// <INet~Services> 1C Group IIS
|
|
// <domain> 1D Unique Master Browser
|
|
// <domain> 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; i<MAX_IPADDRS_PER_HOST; i++)
|
|
{
|
|
IpAddrsList[i] = IpList[i];
|
|
if (IpAddrsList[i] == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
IpAddrsList[MAX_IPADDRS_PER_HOST] = 0;
|
|
|
|
pClientTracker->Flags |= 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
|