windows-nt/Source/XPSP1/NT/net/wins/server/msc/winsprs.c
2020-09-26 16:20:57 +08:00

1933 lines
44 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// NOTE:
//
// Unless the data read from the STATIC file is converted to UNICODE, we should
// not have UNICODE defined for this module
//
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
winsprs.c
Abstract:
This source contains the functions that parse the lmhosts file.
Functions:
GetTokens,
IsKeyWord,
Fgets,
PrimeDb
ExpandName,
RegOrdinaryName,
RegGrpName
WinsPrsDoStaticInit
Portability:
This module is portable
Author:
Pradeep Bahl (PradeepB) Apr-1993
Stole the parsing code from lm_parse.c, lm_io.c, and lm_parse.c in
streams\tcpip\nbt; Modified it appropriately
Revision History:
Modification date Person Description of modification
----------------- ------- ----------------------------
--*/
/*
* Includes
*/
#include <ctype.h>
#include <string.h>
#include "wins.h"
#include "nms.h" //required for DBGPRINT statements
#include <winuser.h>
#include "winsevt.h"
#include "winsprs.h"
#include "winsmsc.h"
#include "nmsmsgf.h"
#include "nmsnmh.h"
#include "comm.h"
#include "winsintf.h"
/*
* Local Macro Declarations
*/
#define DOMAIN_TOKEN "#DOM:"
#define PRELOAD_TOKEN "#PRE"
#define INCLUDE_TOKEN "#INCLUDE"
#define BEG_ALT_TOKEN "#BEGIN_ALTERNATE"
#define END_ALT_TOKEN "#END_ALTERNATE"
#define DOMAIN_TOKEN_SIZE (sizeof(DOMAIN_TOKEN) - 1)
//
// To mark special groups in the lmhosts file
//
#define SPEC_GRP_TOKEN "#SG:"
#define SPEC_GRP_TOKEN_SIZE (sizeof(SPEC_GRP_TOKEN) - 1)
//
// To indicate an mh node
//
#define MH_TOKEN "#MH"
#define MH_TOKEN_SIZE (sizeof(MH_TOKEN) - 1)
#define QUOTE_CHAR '"'
#define TAB_CHAR '\t'
#define SPACE_CHAR ' '
#define CARRIAGE_RETURN_CHAR '\r'
#define NEWLINE_CHAR '\n'
#define COMMENT_CHAR '#'
#define BACKSLASH_CHAR '\\'
#define ZERO_CHAR '0'
#define x_CHAR 'x'
#define X_CHAR 'X'
//
// Size of array to hold a non-coded netbios name read from a file (lmhosts)
//
#define NON_CODED_NAME_SIZE 17
/*
* Local Typedef Declarations
*/
//
// Private Definitions
//
typedef struct _FILE_PARAM_T {
PWINSCNF_DATAFILE_INFO_T pDataFile;
DWORD NoOfFiles;
} FILE_PARAM_T, *PFILE_PARAM_T;
//
// GetTokens() parses a line and returns the tokens in the following
// order:
//
typedef enum _TOKEN_ORDER_E {
E_IPADDRESS = 0, // first token
E_NBNAME, // 2nd token
E_GROUPNAME, // 3rd or 4th token
E_NOTUSED, // #PRE, if any
E_MAX_TOKENS // this must be last
} TOKEN_ORDER_E, *PTOKEN_ORDER_E;
//
// If the line category is E_SPEC_GRP, then we have just one token
//
#define SPEC_GRP_TOKEN_POS 0
//
// 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 (ignored by us).
//
//
typedef enum _TYPE_OF_LINE_E {
E_COMMENT = 0x0000, // comment line
E_ORDINARY = 0x0001, // ip_addr NetBIOS name
E_DOMAIN = 0x0002, // ... #DOM:name
E_INCLUDE = 0x0003, // #INCLUDE file
E_BEGIN_ALTERNATE = 0x0004, // #BEGIN_ALTERNATE
E_END_ALTERNATE = 0x0005, // #END_ALTERNATE
E_SPEC_GRP = 0x0006, // #Spec Grp
E_SGWADD = 0x0007, // #Spec Grp with add
E_PRELOAD = 0x8000, // ... #PRE
E_MH = 0x8001 // ip_addr NetBIOS name
// for a mh machine
} TYPE_OF_LINE_E, *PTYPE_OF_LINE_E;
//
// 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_T { // reserved keyword
LPBYTE pKString; // NULL terminated
size_t KStrlen; // length of token
TYPE_OF_LINE_E KType_e; // type of line
DWORD KNoOfOperands; // max operands on line
} KEYWORD_T, *PKEYWORD_T;
//
// Information about the type of line read is kept in the LINE_CHARACTERISTICS
// structure
//
typedef struct _LINE_CHARACTERISTICS_T
{
int LineCategory:4; // enum _TYPE_OF_LINE
int LinePreload:1; // marked with #PRE ?
int Mh:1; // marked with #MH ?
} LINE_CHARACTERISTICS_T, *PLINE_CHARACTERISTICS_T;
/*
* Global Variable Definitions
*/
/*
* Local Variable Definitions
*/
//
// 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_T Decoration[] = {
DOMAIN_TOKEN, sizeof(DOMAIN_TOKEN) - 1, E_DOMAIN, 4,
PRELOAD_TOKEN, sizeof(PRELOAD_TOKEN) - 1, E_PRELOAD, 4,
SPEC_GRP_TOKEN, sizeof(SPEC_GRP_TOKEN) - 1, E_SGWADD, 4,
MH_TOKEN, sizeof(MH_TOKEN) - 1, E_MH, 4,
NULL, 0 // must be last
};
KEYWORD_T Directive[] = {
INCLUDE_TOKEN, sizeof(INCLUDE_TOKEN) - 1, E_INCLUDE, 2,
BEG_ALT_TOKEN, sizeof(BEG_ALT_TOKEN) - 1, E_BEGIN_ALTERNATE, 1,
END_ALT_TOKEN, sizeof(END_ALT_TOKEN) - 1, E_END_ALTERNATE, 1,
SPEC_GRP_TOKEN, sizeof(SPEC_GRP_TOKEN) - 1, E_SPEC_GRP, 1,
NULL, 0 // must be last
};
/*
* Local Function Prototype Declarations
*/
/* prototypes for functions local to this module go here */
//
// Local (Private) Functions
//
STATIC
BOOL
ChkAdd(
LPBYTE pstrAdd,
LPDWORD pAdd
);
STATIC
LINE_CHARACTERISTICS_T
GetTokens (
IN OUT LPBYTE pLine,
OUT LPBYTE *ppToken,
IN OUT LPDWORD pNumTokens
);
STATIC
PKEYWORD_T
IsKeyWord (
IN LPBYTE pString,
IN PKEYWORD_T pTable
);
STATIC
LPBYTE
Fgets (
PWINSPRS_FILE_INFO_T pFileInfo,
LPDWORD pCount
);
STATIC
VOID
PrimeDb (
PWINSPRS_FILE_INFO_T pFileInfo
);
STATIC
BOOL
ExpandName (
OUT LPBYTE pDest,
IN LPBYTE pSrc,
IN BYTE LastCh,
OUT LPBOOL pfQuoted
);
STATIC
VOID
CheckForInt(
IN OUT LPBYTE pDest,
IN BOOL fQuoted
);
STATIC
VOID
RegOrdinaryName(
LPBYTE pName,
DWORD IpAdd
);
VOID
RegGrpName(
LPBYTE pName,
DWORD IpAdd,
DWORD TypeOfRec
);
STATIC
DWORD
DoStaticInitThdFn(
IN LPVOID pThdParam
);
STATIC
LINE_CHARACTERISTICS_T
GetTokens (
IN OUT LPBYTE pLine,
OUT LPBYTE *ppToken,
IN OUT LPDWORD pNumTokens
)
/*++
Routine Description:
This function parses a line for tokens. A maximum of *pnumtokens
are collected.
Arguments:
pLine - pointer to the NULL terminated line to parse
pToken - 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_E
{ // current fsm state
E_START_OF_LINE,
E_WHITESPACE,
E_TOKEN
} State_e;
LPBYTE pCh; // current fsm input
//LPBYTE pByte; // current fsm input
PKEYWORD_T pKeyword;
DWORD Index;
DWORD MaxTokens;
LINE_CHARACTERISTICS_T Retval;
BOOL fQuoteSeen = FALSE;
BOOL fBreakOut = FALSE;
//
// Zero out the token array
//
RtlZeroMemory(ppToken, *pNumTokens * sizeof(LPBYTE *));
State_e = E_START_OF_LINE;
Retval.LineCategory = E_ORDINARY;
Retval.LinePreload = 0;
Retval.Mh = 0;
MaxTokens = *pNumTokens;
Index = 0;
for (pCh = pLine; *pCh != (BYTE)NULL && !fBreakOut; pCh++)
{
switch ((int)*pCh)
{
//
// does the '#' signify the start of a reserved keyword, or the
// start of a comment ?
//
case COMMENT_CHAR:
//
// if a quote character has been seen earlier, skip this
// char
//
if(fQuoteSeen)
{
continue;
}
//
// See if we have a keyword. Use the appropriate table for the
// lookup
//
pKeyword = IsKeyWord(
pCh,
(State_e == E_START_OF_LINE) ?
Directive : Decoration
);
//
// If it is a keyword
//
if (pKeyword)
{
State_e = E_TOKEN;
MaxTokens = pKeyword->KNoOfOperands;
switch (pKeyword->KType_e)
{
case E_PRELOAD:
Retval.LinePreload = 1;
continue;
case E_MH:
Retval.Mh = 1;
continue;
//
// It is one of the other keywords
//
default:
ASSERT(Index < MaxTokens);
ppToken[Index++] = pCh;
Retval.LineCategory = pKeyword->KType_e;
continue;
}
ASSERT(0);
}
//
// Since it is not a keyword, it is a comment
//
if (State_e == E_START_OF_LINE)
{
Retval.LineCategory = E_COMMENT;
}
/* fall through */
case CARRIAGE_RETURN_CHAR:
case NEWLINE_CHAR:
*pCh = (BYTE) NULL;
fBreakOut = TRUE;
break; //break out of the loop. We are done
case SPACE_CHAR:
case TAB_CHAR:
//
// if State is Token, and there is no ending quote to worry about
// we change the state to WhiteSpace
//
if (State_e == E_TOKEN)
{
if (!fQuoteSeen)
{
State_e = E_WHITESPACE;
*pCh = (BYTE)NULL;
//
// If we have accumulated the desired number of tokens
// break out of the loop
//
if (Index == MaxTokens)
{
fBreakOut = TRUE;
break;
}
}
}
continue;
case QUOTE_CHAR:
//
// Check whether we have seen the beginning quote char earlier
//
if(fQuoteSeen)
{
//
// Ending quote consumed. Set flag to FALSE
//
fQuoteSeen = FALSE;
}
else // companion quote not seen earlier
{
//
// This could be the starting quote of the #DOM:
// keyword's string or could be the starting
// quote of the nbtname string
//
if (State_e == E_TOKEN)
{
//
// It is the starting quote of the #DOM: keyword
// string
//
// --ft: the statement above doesn't stand for legal LMHOSTS lines like:
// #SG:"SGNoMember"
// so I commented out the assert below:
//ASSERT(Index > E_NBNAME);
}
else
{
//
// Must be the starting quote of the Nbt name
//
ASSERT(Index == E_NBNAME);
State_e = E_TOKEN;
//
// Store the pointer to the token
//
ppToken[Index++] = pCh;
}
fQuoteSeen = TRUE;
}
continue;
default:
//
// If this is the token state, continue
//
if (State_e == E_TOKEN)
{
continue;
}
ASSERT(Index < MaxTokens);
State_e = E_TOKEN;
//
// Store the pointer to the token
//
ppToken[Index++] = pCh;
continue;
} // end of switch
} // end of for loop
*pNumTokens = Index;
return(Retval);
} // GetTokens
STATIC
PKEYWORD_T
IsKeyWord (
IN LPBYTE pString,
IN PKEYWORD_T pKTable
)
/*++
Routine Description:
This function determines whether the string is a reserved keyword.
Arguments:
pString - the string to search
pKTable - an array of keywords to look for
Return Value:
A pointer to the relevant keyword object, or NULL if unsuccessful
--*/
{
size_t StringSize;
PKEYWORD_T pSpecial;
StringSize = strlen(pString);
for (pSpecial = pKTable; pSpecial->pKString; pSpecial++) {
//
// If the length of the string is less than that of the keyword,
// go on to the next keyword in the table
//
if (StringSize < pSpecial->KStrlen)
{
continue;
}
//
// if length of string is greater than or equal to the keyword
// length and the string matches the keyword in the # of characters
// that comprise the keywordm, return the address of the keyword
// structure
//
FUTURES("use lstrncmp when it becomes available")
if (
(StringSize >= pSpecial->KStrlen)
&&
!strncmp(pString, pSpecial->pKString, pSpecial->KStrlen)
)
{
return(pSpecial);
}
}
return((PKEYWORD_T) NULL);
} // IsKeyWord
VOID
PrimeDb (
PWINSPRS_FILE_INFO_T pFileInfo
)
/*++
Routine Description:
This function primes the WINS db
Arguments:
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
LPBYTE CurrLine;
DWORD Count;
DWORD NWords;
LPBYTE ppToken[E_MAX_TOKENS];
LINE_CHARACTERISTICS_T CurrLineChar;
DWORD Add;
BOOL fBadAdd;
DWORD TkSize;
DWORD TypeOfRec;
try {
//
// Loop over all records
//
pFileInfo->pCurrPos = pFileInfo->pFileBuff;
while (CurrLine = Fgets(pFileInfo, &Count) )
{
NWords = E_MAX_TOKENS;
fBadAdd = FALSE;
CurrLineChar = GetTokens(CurrLine, ppToken, &NWords);
switch (CurrLineChar.LineCategory)
{
case E_SGWADD:
TypeOfRec = NMSDB_USER_SPEC_GRP_ENTRY;
TkSize = SPEC_GRP_TOKEN_SIZE;
//fall through
case E_DOMAIN:
if (CurrLineChar.LineCategory == E_DOMAIN)
{
TypeOfRec = NMSDB_SPEC_GRP_ENTRY;
TkSize = DOMAIN_TOKEN_SIZE;
}
//
// If there are too few words in the line, go
// get the next line
//
if ((NWords - 1) < E_GROUPNAME)
{
continue;
}
if (ChkAdd(ppToken[E_IPADDRESS], &Add))
{
//
// Register the domain name (group name with 1C at
// the end)
//
RegGrpName(
ppToken[E_GROUPNAME] + TkSize,
Add,
TypeOfRec
);
}
else
{
fBadAdd = TRUE;
}
//
// Fall through
//
case E_ORDINARY:
//
// If there are too few words in the line, go
// get the next line
//
// Don't use (NWords - 1) < E_NBNAME since
// NWords can be 0 in which case the test will
// fail
//
if (NWords < (E_NBNAME + 1))
{
continue;
}
else
{
if (!fBadAdd && ChkAdd(ppToken[E_IPADDRESS], &Add))
{
if (CurrLineChar.Mh)
{
RegGrpName(
ppToken[E_NBNAME],
Add,
NMSDB_MULTIHOMED_ENTRY
);
}
else
{
//
// register the name
//
RegOrdinaryName( ppToken[E_NBNAME], Add);
}
}
else
{
WinsMscLogEvtStrs(
ppToken[E_NBNAME],
WINS_EVT_BAD_ADDRESS,
FALSE
);
DBGPRINT2(ERR, "PrimeDb: Name (%s) has bad address = (%s). It is being ignored\n", ppToken[E_NBNAME], ppToken[E_IPADDRESS]);
}
}
continue;
case E_SPEC_GRP:
//
// Register the domain name (group name with 1C at
// the end)
//
RegGrpName(
ppToken[SPEC_GRP_TOKEN_POS] + SPEC_GRP_TOKEN_SIZE,
0,
NMSDB_USER_SPEC_GRP_ENTRY);
continue;
case E_INCLUDE: // fall through
case E_BEGIN_ALTERNATE: // fall through
case E_END_ALTERNATE: // fall through
continue;
default:
continue;
}
}
} // end of try block
finally {
//
// deallocate the memory to which the file was mapped
//
WinsMscDealloc(pFileInfo->pFileBuff);
}
return;
} // PrimeDb
LPBYTE
Fgets (
IN PWINSPRS_FILE_INFO_T pFileInfo,
OUT LPDWORD pNoOfCh
)
/*++
Routine Description:
This function is vaguely similar to fgets(3).
Starting at the current seek position, it reads through a newline
character, or the end of the file. If a newline is encountered, it
is replaced with a NULL character.
Arguments:
pfile - file to read from
nbytes - the number of characters read, excluding the NULL character
Return Value:
A pointer to the beginning of the line, or NULL if we are at or past
the end of the file.
--*/
{
LPBYTE pEndOfLine;
LPBYTE pStartOfLine;
SIZE_T MaxCh;
//
// Store the current position in the memory buffer
//
pStartOfLine = (LPBYTE)pFileInfo->pCurrPos;
//
// If it is greater or equal than the limit, return NULL
//
if (pStartOfLine >= (LPBYTE)pFileInfo->pLimit) {
return(NULL);
}
//
// Store the max. number of bytes between the current position and
// the end of the buffer.
//
MaxCh = (pFileInfo->pLimit - pFileInfo->pCurrPos);
//
// get to the end of the line
//
pEndOfLine = (LPBYTE)memchr(pStartOfLine, NEWLINE_CHAR, (size_t)MaxCh);
if (!pEndOfLine)
{
DBGPRINT0(FLOW, "Data file does not end in newline\n");
return(NULL);
}
*pEndOfLine = (BYTE)NULL;
pFileInfo->pCurrPos = pEndOfLine + 1; //adjust the pointer
ASSERT(pFileInfo->pCurrPos <= pFileInfo->pLimit);
*pNoOfCh = (DWORD) (pEndOfLine - pStartOfLine);
return(pStartOfLine);
} // Fgets
VOID
RegOrdinaryName(
IN LPBYTE pName,
IN DWORD IPAdd
)
/*++
Routine Description:
This function registers a unique name
Arguments:
pName - Name to register
IPAdd - Address to register
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
BYTE Dest[WINS_MAX_LINE_SZ];
BOOL fQuoted;
COMM_ADD_T NodeAdd;
LPBYTE pDest = Dest;
NodeAdd.AddLen = sizeof(COMM_IP_ADD_T);
NodeAdd.AddTyp_e = COMM_ADD_E_TCPUDPIP;
NodeAdd.Add.IPAdd = IPAdd;
//
// Form the name. If the name is < 16 characters, 0x20 will
// be put in the Sixteenth bytes
//
if (!ExpandName(Dest, pName, 0x20, &fQuoted))
{
DBGPRINT1(ERR, "Name (%s) has more than 16 characters\n", pName);
return;
}
NMSMSGF_MODIFY_NAME_IF_REQD_M(Dest);
NmsNmhNamRegInd(
NULL,
Dest,
strlen(Dest) + 1, //we always store the terminating
//NULL
&NodeAdd,
NMSMSGF_E_BNODE,
NULL,
0,
0,
FALSE, //it is a name registration (not a refresh)
NMSDB_ENTRY_IS_STATIC,
0 //not an administrative action
);
//
// If the name was not quoted, register the other two records
// (same name -- different suffixes)
//
if(!fQuoted)
{
#if 0
if (*pDest == 0x1B)
{
WINS_SWAP_BYTES_M(pDest, pDest + 15);
}
#endif
Dest[NON_CODED_NAME_SIZE - 2] = 0x3;
NmsNmhNamRegInd(
NULL,
Dest,
strlen(Dest) + 1, //we always store the terminating
//NULL
&NodeAdd,
NMSMSGF_E_BNODE,
NULL,
0,
0,
FALSE, //it is a name registration (not a refresh)
NMSDB_ENTRY_IS_STATIC,
0 //not an administrative action
);
Dest[NON_CODED_NAME_SIZE - 2] = 0x0;
NmsNmhNamRegInd(
NULL,
Dest,
strlen(Dest) + 2, //add 1 since terminating 0x0 is
//to be stored (will be taken
//as NULL by strlen
&NodeAdd,
NMSMSGF_E_BNODE,
NULL,
0,
0,
FALSE, //it is a name registration (not a refresh)
NMSDB_ENTRY_IS_STATIC,
0 //not an administrative action
);
}
return;
}
VOID
RegGrpName(
IN LPBYTE pName,
IN DWORD IPAdd,
IN DWORD TypeOfRec
)
/*++
Routine Description:
This function registers a domain name
Arguments:
pName - Name to register
IpAdd - Address to register
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
BYTE Dest[WINS_MAX_LINE_SZ];
BOOL fQuoted;
NMSMSGF_CNT_ADD_T CntAdd;
DWORD RecType;
BYTE SixteenthByte;
if (*pName == EOS)
{
WinsMscLogEvtStrs(pName, WINS_EVT_NAME_FMT_ERR, FALSE);
return;
}
//
// don't want 0 ip address to be put in
//
if (IPAdd)
{
CntAdd.NoOfAdds = 1;
CntAdd.Add[0].AddLen = sizeof(COMM_IP_ADD_T);
CntAdd.Add[0].AddTyp_e = COMM_ADD_E_TCPUDPIP;
CntAdd.Add[0].Add.IPAdd = IPAdd;
}
else
{
CntAdd.NoOfAdds = 0;
}
if (TypeOfRec != NMSDB_SPEC_GRP_ENTRY)
{
SixteenthByte = 0x20;
}
else
{
SixteenthByte = 0x1C;
}
//
// We can get called for domains, user. spec. grps and mh names.
//
RecType = (TypeOfRec == NMSDB_USER_SPEC_GRP_ENTRY) ?
NMSDB_SPEC_GRP_ENTRY : TypeOfRec;
//
// If the name length is < 16 characters, 0x20 or 0x1C will be put in
// the Sixteenth byte
//
if (!ExpandName(Dest, pName, SixteenthByte, &fQuoted))
{
return;
}
if (RecType == NMSDB_MULTIHOMED_ENTRY)
{
//
// switch the 1st and Sixteenth bytes if the Sixteenth byte is a 0x1B. This
// is done only for non-group names
//
NMSMSGF_MODIFY_NAME_IF_REQD_M(Dest);
}
//
// register the group
//
NmsNmhNamRegGrp(
NULL,
Dest,
strlen(Dest) + 1, // to store the null
&CntAdd,
0, //Node type (not used)
NULL,
0,
0,
RecType,
FALSE, //it is a name registration (not a refresh)
NMSDB_ENTRY_IS_STATIC,
0 //not an administrative action
);
if (RecType == NMSDB_MULTIHOMED_ENTRY)
{
//
// If the name was not quoted, register the other two records
// (same name -- different suffixes)
//
if(!fQuoted)
{
Dest[NON_CODED_NAME_SIZE - 2] = 0x3;
NmsNmhNamRegGrp(
NULL,
Dest,
strlen(Dest) + 1, // to store the null
&CntAdd,
0, //Node type (not used)
NULL,
0,
0,
RecType,
FALSE, //it is a name registration (not a refresh)
NMSDB_ENTRY_IS_STATIC,
0 //not an administrative action
);
Dest[NON_CODED_NAME_SIZE - 2] = 0x0;
NmsNmhNamRegGrp(
NULL,
Dest,
strlen(Dest) + 2, // to store the null
&CntAdd,
0, //Node type (not used)
NULL,
0,
0,
RecType,
FALSE, //it is a name registration (not a refresh)
NMSDB_ENTRY_IS_STATIC,
0 //not an administrative action
);
}
}
return;
}
BOOL
ExpandName (
OUT LPBYTE pDest,
IN LPBYTE pSrc,
IN BYTE LastCh,
OUT LPBOOL pfQuoted
)
/*++
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 Sixteenth byte is the
input parameter, last.
Both dest and source are NULL terminated strings.
Arguments:
pDest - sizeof(dest) must be WINSPRS_NONCODED_NMSZ
pSrc - the lmhosts entry
LastCh - the Sixteenth byte of the NetBIOS name
pfQuoted - flag to indicate whether the string was quoted or not
Return Value:
None
--*/
{
BYTE Ch;
DWORD i = 0;
LPBYTE pFrom = pSrc;
LPBYTE pTo = pDest;
// Detect if it is quoted name..
*pfQuoted = (*pFrom == QUOTE_CHAR);
// ..and skip the initial quote char
pFrom += *pfQuoted;
// count for as many chars as are in a legal NetBios name (15) plus
// the terminating char. (NON_CODED_NAME_SIZE is #defined to be 17)
for (i = 0; i < NON_CODED_NAME_SIZE - 1; i++)
{
// get the next char from the name
Ch = *(pFrom++);
// check if it is a terminating char
if (!Ch || (*pfQuoted ? Ch == QUOTE_CHAR : Ch == NEWLINE_CHAR))
break;
// check if the name doesn't exceed the legal 15 chars
if (i == NON_CODED_NAME_SIZE - 2)
{
// We have picked up 15 characters already and there are more in the name
// This is illegal so log error and bail out
DBGPRINT1(ERR, "Name (%s) has more than 16 characters\n", pSrc);
WinsMscLogEvtStrs(pSrc, WINS_EVT_BAD_NAME, FALSE);
return FALSE;
}
// If the char is a leading DBCS byte then accept the extended char as it
// is (take the trailing byte and go on.
if (IsDBCSLeadByteEx(CP_ACP, Ch))
{
*pTo++ = Ch;
*pTo++ = *pFrom++;
continue;
}
// If the name is not quoted, map lower case alpha chars to upper case.
// Note: don't use _toupper since _toupper does not check whether its
// argument is indeed a lowercase char.
if (!*pfQuoted && IsCharAlpha(Ch))
{
if(IsCharLower(Ch))
{
LPBYTE pCh;
BYTE sCh[2];
sCh[0] = Ch;
sCh[1] = (BYTE)NULL;
pCh = (LPBYTE)CharUpperA(sCh);
Ch = *pCh;
}
}
// Check if we have hex value in the name
if (Ch == BACKSLASH_CHAR)
{
DWORD NoOfChar;
INT NumValue = 0;
CHAR Ch2;
BOOL fFailed = FALSE;
// the hex value can be either \A3 or \xFC (obviously with any kind of hex digits)
Ch = *pFrom;
Ch2 = *(pFrom+1);
if (Ch == BACKSLASH_CHAR)
{
// '\\' should be seen as one '\', hence keep Ch as it is and break from switch
pFrom++;
}
else
{
if ((Ch == X_CHAR || Ch == x_CHAR) ||
(Ch == ZERO_CHAR && (Ch2 == X_CHAR || Ch2 == x_CHAR))
)
{
DBGPRINT1(TMP, "Parsing hex num %s\n", pFrom);
// skip over x or 0x.
pFrom += (Ch == X_CHAR || Ch == x_CHAR) ? 1 : 2;
// we do have a hex number here. Pick up at most the first two digits.
fFailed = (sscanf(pFrom, "%2x%n", &NumValue, &NoOfChar) == 0 || NoOfChar == 0);
DBGPRINT2(TMP, "fFailed=%d; HexNumValue=0x%x\n", fFailed, NumValue);
}
else
{
DBGPRINT1(TMP, "Parsing dec num %s\n", pFrom);
// it might be a decimal number. Pick up at most the first 3 digits.
fFailed = (sscanf(pFrom, "%3u%n", &NumValue, &NoOfChar) == 0 || NoOfChar == 0 || NumValue > 255);
DBGPRINT2(TMP, "fFailed=%d; DecNumValue=%u\n", fFailed, NumValue);
}
if (fFailed)
{
// log an event and bail out with error.
DBGPRINT1(ERR, "Name (%s) contains incorrectly formed character code.\n", pSrc);
WinsMscLogEvtStrs(pSrc, WINS_EVT_BAD_CHARCODING, FALSE);
return FALSE;
}
// everything went fine, copy the hex value back to Ch
Ch = (BYTE)NumValue;
// and make sure to advance on the pFrom string
pFrom += NoOfChar;
}
}
// finally copy the char to the destination string
*pTo = Ch;
// advance with the pointer on the destination string
pTo++;
} //end of for loop
// if there were less than expected char, form the valid netbios name
// by padding it with spaces
for (;i < NON_CODED_NAME_SIZE - 2; i++, pTo++)
*pTo = SPACE_CHAR;
*pTo = (BYTE)NULL;
*(pTo+1) = (BYTE)NULL;
CheckForInt(pDest, *pfQuoted);
// at the end, append the LastCh (16th byte) to the name.
*pTo = LastCh;
return(TRUE);
} // ExpandName
VOID
CheckForInt(
LPBYTE pDest,
BOOL fQuoted
)
/*++
Routine Description:
This function munges the name so that if there are any characters
from a different code set, they are converted properly
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
WCHAR UnicodeBuf[255];
UNICODE_STRING UnicodeStr;
STRING TmpStr;
NTSTATUS NTStatus;
DBGENTER("CheckForInt\n");
//
// Now, convert to UNICODE then to OEM to force the ANSI -> OEM munge.
// Then convert back to UNICODE and uppercase the name. Finally convert
// back to OEM.
//
UnicodeStr.Length = 0;
UnicodeStr.MaximumLength = sizeof(UnicodeBuf);
UnicodeStr.Buffer = UnicodeBuf;
RtlInitString(&TmpStr, pDest);
NTStatus = RtlAnsiStringToUnicodeString(&UnicodeStr, &TmpStr, FALSE);
if (!NT_SUCCESS(NTStatus))
{
DBGPRINT1(ERR, "CheckForInt: Ansi -> Unicode failed, NTStatus %X\n",
NTStatus);
goto ERROR_PROC;
}
NTStatus = RtlUnicodeStringToOemString(&TmpStr, &UnicodeStr, FALSE);
if (!NT_SUCCESS(NTStatus))
{
DBGPRINT1(ERR, "CheckForInt: Unicode -> Oem failed, NTStatus %X\n",
NTStatus);
goto ERROR_PROC;
}
NTStatus = RtlOemStringToUnicodeString(&UnicodeStr, &TmpStr, FALSE);
if (!NT_SUCCESS(NTStatus))
{
DBGPRINT1(ERR, "CheckForInt: Oem -> Unicode failed, NTStatus %X\n",
NTStatus);
goto ERROR_PROC;
}
if (!fQuoted)
NTStatus = RtlUpcaseUnicodeStringToOemString(&TmpStr, &UnicodeStr, FALSE);
else
NTStatus = RtlUnicodeStringToOemString(&TmpStr, &UnicodeStr, FALSE);
if (!NT_SUCCESS(NTStatus))
{
DBGPRINT1(ERR, "CheckForInt: Unicode -> Oem failed, NTStatus %X\n",
NTStatus);
goto ERROR_PROC;
}
ERROR_PROC:
DBGLEAVE("CheckForInt\n");
return;
}
STATUS
WinsPrsDoStaticInit(
IN PWINSCNF_DATAFILE_INFO_T pDataFile,
IN DWORD NoOfFiles,
IN BOOL fAsync
)
/*++
Routine Description:
This function is called to do the STATIC initialization of the WINS
db
Arguments:
pDataFiles - Pointer to buffer containing one or more data file
structures (PWINSCNF_DATAFILE_INFO_T)
Externals Used:
None
Return Value:
None
Error Handling:
Called by:
Init()
Side Effects:
Comments:
None
--*/
{
DWORD ThdId;
PFILE_PARAM_T pFileParam;
STATUS RetStat = WINS_SUCCESS;
HANDLE sThdHdl = NULL;
try {
WinsMscAlloc(sizeof(FILE_PARAM_T), &pFileParam);
pFileParam->pDataFile = pDataFile;
pFileParam->NoOfFiles = NoOfFiles;
if (fAsync)
{
sThdHdl =
WinsMscCreateThd(DoStaticInitThdFn, pFileParam, &ThdId);
//
// We don't need the handle, so let us close it
//
CloseHandle(sThdHdl);
}
else
{
RetStat = DoStaticInitThdFn(pFileParam);
}
}
except(EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("WinsPrsDoStaticInit");
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_STATIC_INIT_ERR);
}
return(RetStat);
}
DWORD
DoStaticInitThdFn(
IN LPVOID pThdParam
)
/*++
Routine Description:
This thread reads one or more files to do STATIC initialization
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
WINSPRS_FILE_INFO_T FileInfo;
DWORD i;
PWINSCNF_DATAFILE_INFO_T pDataFile =
(((PFILE_PARAM_T)pThdParam)->pDataFile);
DWORD NoOfFiles =
(((PFILE_PARAM_T)pThdParam)->NoOfFiles);
LPVOID pSvDataFilePtr = pDataFile;
DWORD RetStat = WINS_SUCCESS;
//
// initialize this thread with the db engine
//
// This is not an RPC thread. It could have been created either by
// the main thread (doing an init/reinit) or by an rpc thread. For
// either case, we do not want the counter NmsTermThdCnt to be
// incremented in NmsDbThdInit(). Instead of passing a client
// var to indicate which thread invoked it, we call it an RPC
// thread to have NmsDbThdInit do the right thing. NmsDbOpenTables
// will also do the right thing.
//
NmsDbThdInit(WINS_E_WINSRPC);
NmsDbOpenTables(WINS_E_WINSRPC);
EnterCriticalSection(&WinsIntfCrtSec);
WinsIntfNoCncrntStaticInits++;
LeaveCriticalSection(&WinsIntfCrtSec);
try {
for (
i = 0;
i < NoOfFiles;
i++, pDataFile = (PWINSCNF_DATAFILE_INFO_T)((LPBYTE)pDataFile +
WINSCNF_FILE_INFO_SZ)
)
{
//
// Open the file
//
if (
!WinsMscOpenFile(
pDataFile->FileNm,
pDataFile->StrType,
&FileInfo.FileHdl
)
)
{
WINSEVT_STRS_T EvtStrs;
#ifndef UNICODE
DBGPRINT1(ERR, "WinsPrsDoStaticInit: Could not open file= (%s)\n", pDataFile->FileNm);
#else
#ifdef WINSDBG
IF_DBG(ERR)
{
wprintf(L"WinsPrsDoStatisInit: Could not open file = (%s)\n", pDataFile->FileNm);
}
#endif
#endif
EvtStrs.NoOfStrs = 1;
EvtStrs.pStr[0] = pDataFile->FileNm;
WINSEVT_LOG_STR_M(WINS_EVT_CANT_OPEN_DATAFILE, &EvtStrs);
RetStat = WINS_FAILURE;
continue;
}
#ifndef UNICODE
DBGPRINT1(DET, "WinsPrsDoStaticInit: Opened file (%s) for doing STATIC initialization\n", pDataFile->FileNm);
#else
#ifdef WINSDBG
IF_DBG(ERR)
{
wprintf(L"WinsPrsDoStatisInit: Opened file (%s) for doing STATIC initialization\n", pDataFile->FileNm);
}
#endif
#endif
//
// Map the file into allocated memory
//
if(!WinsMscMapFile(&FileInfo))
{
continue;
}
//
// prime the db
//
PrimeDb(&FileInfo);
} // end of for loop
} // end of try ..
except(EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("DoStaticInitThdFn");
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_STATIC_INIT_ERR);
}
EnterCriticalSection(&WinsIntfCrtSec);
WinsIntfNoCncrntStaticInits--;
LeaveCriticalSection(&WinsIntfCrtSec);
//
// Let us end the session
//
try {
NmsDbCloseTables();
NmsDbEndSession();
}
except (EXCEPTION_EXECUTE_HANDLER) {
DBGPRINTEXC("DoStaticInit: During wrap up");
}
//
// Deallocate the memory
//
ASSERT(pSvDataFilePtr != NULL);
WinsMscDealloc(pSvDataFilePtr);
//
// Be sure to deallocate the thread param
//
WinsMscDealloc(pThdParam);
// ExitThread(WINS_SUCCESS);
return(RetStat); //to shutup compiler warning
}
BOOL
ChkAdd(
LPBYTE pstrAdd,
LPDWORD pAdd
)
/*++
Routine Description:
This function converts a dotted decimel ip address to
a DWORD. We don't use inet_addr() to do this, since it
returns 0xFFFFFFFF for an address that has one of its
parts > 255 and returns some value for an invalid address
(for example, one with 3 dots)
Arguments:
Externals Used:
None
Return Value:
Success status codes --
Error status codes --
Error Handling:
Called by:
Side Effects:
Comments:
None
--*/
{
BYTE Tmp[WINS_MAX_LINE_SZ];
DWORD Word[4];
LPBYTE pPos;
DWORD Count = 0;
BOOL fInvalid = FALSE;
//
// We must see three dots
//
while(Count < 4)
{
if ((pPos = strchr(pstrAdd, (int)'.')) != NULL)
{
do
{
//
// Copy all chars before the dot
//
(void)RtlCopyMemory(Tmp, pstrAdd, pPos - pstrAdd);
//
// Put a NULL at the end
//
Tmp[pPos - pstrAdd] = EOS;
Word[Count] = (DWORD)atol(Tmp);
//
//atol returns 0 if it can not convert
//but 0 can be a valid return too (if we have '0' to
// connvert
//
if (Word[Count] == 0)
{
if (Tmp[0] != '0')
{
fInvalid = TRUE;
break;
}
}
else
{
if (Word[Count] > 255)
{
fInvalid = TRUE;
break;
}
}
Count++;
pstrAdd = ++pPos;
} while ((Count == 3) && (pPos = pstrAdd + strlen(pstrAdd)));
if (fInvalid)
{
break;
}
}
else
{
//
// less than 3 dots seen, break out of the loop
//
break;
}
} // end of while (Count < 4)
if ((Count < 4) || fInvalid)
{
return(FALSE);
}
else
{
*pAdd = (LONG)((Word[0] << 24) + (Word[1] << 16) +
(Word[2] << 8) + Word[3]);
}
return(TRUE);
}
#if 0
VOID
GetFullPath(
IN LPBYTE pTarget,
OUT LPBYTE pPath
)
/*++
Routine Description:
This function returns the full path of the STATIC file. This is done
by forming a unicode string from the concatenation of the C strings
DatabasePath and the string, file.
You must RtlFreeUnicodeString(path) after calling this function
successfully !
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 UNICODE_STRING structure
Return Value:
STATUS_SUCCESS if successful.
Notes:
RtlMoveMemory() handles overlapped copies; RtlCopyMemory() doesn't.
--*/
{
NTSTATUS status;
ULONG unicodesize;
STRING directory, file, prefix, remote;
RtlInitString(&prefix, "\\DosDevices");
RtlInitString(&remote, "\\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
// default directory, DatabasePath, to create a full path.
//
//
if ((*target == '\\') || (target[1] == ':')) {
RtlInitString(&directory, target);
RtlInitString(&file, NULL);
}
else {
RtlInitString(&directory, DatabasePath);
RtlInitString(&file, target);
}
ASSERT(RtlAnsiStringToUnicodeSize(&prefix) <=
RtlAnsiStringToUnicodeSize(&remote));
unicodesize = RtlAnsiStringToUnicodeSize(&remote) +
RtlAnsiStringToUnicodeSize(&directory) +
RtlAnsiStringToUnicodeSize(&file) +
2 * sizeof(OBJ_NAME_PATH_SEPARATOR) +
sizeof(UNICODE_NULL);
path->Length = 0;
path->MaximumLength = (USHORT) unicodesize;
path->Buffer = ExAllocatePool(NonPagedPool, unicodesize);
if (!path->Buffer) {
return(STATUS_NO_MEMORY);
}
//
// does the directory specify a DOS drive ?
//
// If the second character of directory is a colon, then it must specify
// a DOS drive. If so, it must be prefixed with "\\DosDevices".
//
//
if (directory.Buffer[1] == ':') {
status = LmpConcatenate(path, &prefix);
if (status != STATUS_SUCCESS) {
path->MaximumLength = 0;
ExFreePool(path->Buffer);
return(status);
}
}
//
// 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 ((directory.Buffer[0] == '\\') && (directory.Buffer[1] == '\\')) {
status = LmpConcatenate(path, &remote);
if (status != STATUS_SUCCESS) {
path->MaximumLength = 0;
ExFreePool(path->Buffer);
return(status);
}
directory.Length--;
ASSERT(((ULONG) directory.Length - 1) > 0);
RtlMoveMemory( // overlapped copy
&(directory.Buffer[1]), // Destination
&(directory.Buffer[2]), // Source
(ULONG) directory.Length - 1); // Length
}
//
// is the first part of the directory "%SystemRoot%" ?
//
// If so, it must be changed to "\\SystemRoot\\".
//
// 0123456789 123456789 1
// %SystemRoot%\somewhere
//
//
if (strncmp(directory.Buffer, "%SystemRoot%", 12) == 0) {
directory.Buffer[0] = '\\';
directory.Buffer[11] = '\\';
if (directory.Buffer[12] == '\\') {
ASSERT(directory.Length >= 13);
if (directory.Length > 13) {
RtlMoveMemory( // overlapped copy
&(directory.Buffer[12]), // Destination
&(directory.Buffer[13]), // Source
(ULONG) directory.Length - 13); // Length
directory.Buffer[directory.Length - 1] = (CHAR) NULL;
}
directory.Length--;
}
}
status = LmpConcatenate(path, &directory);
if (status != STATUS_SUCCESS) {
path->MaximumLength = 0;
ExFreePool(path->Buffer);
return(status);
}
if (!(file.Length)) {
return(status);
}
status = LmpConcatenate(path, &file);
if (status != STATUS_SUCCESS) {
path->MaximumLength = 0;
ExFreePool(path->Buffer);
return(status);
}
return(STATUS_SUCCESS);
} // LmGetFullPath
#endif