// 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 #include #include "wins.h" #include "nms.h" //required for DBGPRINT statements #include #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