/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

        winscnf.c

Abstract:

        This module contains functions that deal with the configuration
        information for the WINS

Portability:

        This module is portable

Author:

        Pradeep Bahl (PradeepB)          Dec-1992


Revision History:

        Modification date        Person                Description of modification
        -----------------        -------                ----------------------------
--*/

/*
 *       Includes
*/

#include "wins.h"
#include <winsock2.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <search.h>
#include "comm.h"
#include "winreg.h"
#include "winsevt.h"
#include "winsmsc.h"
#include "winscnf.h"
#include "nms.h"
#include "nmsnmh.h"
#include "rpl.h"
#include "rplpush.h"
#include "winsintf.h"
#include "nmfilter.h"
#include <resapi.h>


/*
 *        Local Macro Declarations
*/

//
// Size of max string that a user can input in a REG_SZ field
//
#define  MAX_SZ_SIZE        80

#define REG_M(fn, evt, exc)                                        \
                        {                                        \
                           if((fn) != ERROR_SUCCESS)                \
                           {                                        \
                                WINSEVT_LOG_M(                        \
                                        WINS_FATAL_ERR,                \
                                        (evt)                        \
                                                   );                \
                                WINS_RAISE_EXC_M((exc));        \
                           }                                        \
                        }

//
// pointer to default path for log file.  If you change this to a NON NULL
// value, make sure you don't try to free the memory in SetSystemParam
// in nmsdb.c
//
#define DEFAULT_LOG_PATH         NULL

#define  _WINS_CFG_KEY                \
                TEXT("System\\CurrentControlSet\\Services\\Wins")
#define  _WINS_CFG_PARAMETERS_KEY        TEXT("Parameters")
#define  _WINS_CFG_PARTNERS_KEY          TEXT("Partners")
#define  _WINS_CFG_CC_KEY                TEXT("Parameters\\ConsistencyCheck")
#define  _WINS_CFG_PULL_KEY              TEXT("Partners\\Pull")
#define  _WINS_CFG_PUSH_KEY              TEXT("Partners\\Push")
#define  _WINS_CFG_DATAFILES_KEY         TEXT("Parameters\\Datafiles")
#define  _WINS_CFG_SPEC_GRP_MASKS_KEY    TEXT("Parameters\\InternetGrpMasks")

#define  _WINS_LOG_KEY                \
                TEXT("System\\CurrentControlSet\\Services\\EventLog\\Application\\WinsInt")
#define  _WINS_MSGFILE_SKEY  TEXT("EventMessageFile")


#define  _WINS_LOG_FILE_NAME TEXT("%SystemRoot%\\System32\\winsevnt.dll")


#define   _RPL_CLASS                TEXT("RplClass")//class for Rpl Pull and Push
                                                //keys


//
// The start version number should never be allowed to go above this number
// This will avoid a wrap around.
//

#define MAX_START_VERS_NO     0x0FFFFFFF

//
// Names of event variables used for notification purposes
//
#ifdef WINSDBG
#define        WINS_KEY_CHG_EVT_NM                TEXT("WinsKChgEvt")
#define        PARAMETERS_KEY_CHG_EVT_NM        TEXT("WinsParamatersKChgEvt")
#define        PARTNERS_KEY_CHG_EVT_NM                TEXT("WinsPartenersKChgEvt")
#define CNF_CHG_EVT_NM                        TEXT("WinsConfigChangeEvt")
#else
#define        WINS_KEY_CHG_EVT_NM                NULL
#define        PARAMETERS_KEY_CHG_EVT_NM        NULL
#define        PARTNERS_KEY_CHG_EVT_NM                NULL
#define CNF_CHG_EVT_NM                        NULL
#endif

//
// Values for the fStaticInit field of the configuration data structure
//
#define  DO_STATIC_INIT                        TRUE
#define  DONT_DO_STATIC_INIT                FALSE


//
// defines for the InitTimeRpl and InitTimePush fields of WinsCnf
//
#define DO_INIT_TIME_RPL                1
#define NO_INIT_TIME_RPL                0


//
//  NO_LIMIT_CHK_FLAG  - for easing the task of testers
//
//  If this flag is set in LogDetailedEvts DWORD, WINS skips all
//  checks for the min. values of the time intervals and Update Count.
//  This kind of operation of WINS is unsupported and is being provided
//  only to help out testers
//
#define  NO_LIMIT_CHK_FLAG   0x80000000    //MSB is set.


//
// If ErrEvt passed is 0, we don't log any message.  NOTE: a WINS event
// can never have 0 as its value (checkout winsevnt.mc)
//
#define QUERY_VALUE_M(Key, Str, ValTyp, Var, ErrEvt, DefVal)                \
        {                                                                \
                DWORD Sz = sizeof((Var));                                \
                if (RegQueryValueEx(                                        \
                                (Key),                                        \
                                (Str),                                        \
                                NULL,                                               \
                                &(ValTyp),                                \
                                (LPBYTE)&(Var),                                \
                                &Sz                                        \
                                ) != ERROR_SUCCESS                        \
                    )                                                        \
                {                                                        \
                        if ((ErrEvt) != 0)                                \
                        {                                                \
                                WINSEVT_LOG_INFO_M(                        \
                                        WINS_SUCCESS,                        \
                                        (ErrEvt)                        \
                                       );                                \
                        }                                                \
                        Var = DefVal;                                        \
                }                                                        \
           }
/*
 *        Local Typedef Declarations
 */

/*
 *        Global Variable Definitions
 */

CRITICAL_SECTION  WinsCnfCnfCrtSec;                //used for reinitialization
                                                //of certain fields of the
                                                //WinsCnf structure


BOOL        fWinsCnfRplEnabled = TRUE;                //replication is enabled
BOOL        fWinsCnfScvEnabled = TRUE;                //scavenging is enabled

FUTURES("use #ifdef PERF around the following three perf. mon. vars")
BOOL        fWinsCnfPerfMonEnabled   = FALSE;        //perf. mon is disabled
BOOL          fWinsCnfHighResPerfCntr = FALSE;    //indicates whether the
                                                  //hardware supports a high
                                                  //performance counter
LARGE_INTEGER LiWinsCnfPerfCntrFreq;                     //indicates the frequency of
                                                  //the counter

BOOL    fWinsCnfReadNextTimeVersNo = FALSE;
DWORD        WinsCnfCnfMagicNo          = WINSCNF_INITIAL_CNF_MAGIC_NO;
BOOL    fWinsCnfInitStatePaused;

//TCHAR        WinsCnfDb[WINS_MAX_FILENAME_SZ];   //db file to hold tables
BOOL    WinsCnfRegUpdThdExists = FALSE;
//#define MAX_PATH_SIZE        200
PTCHAR  pWinsCnfNbtPath;


BOOL   sfNoLimitChk = FALSE;   //to override the limit checks
//
// NetBt handle
//
HANDLE        WinsCnfNbtHandle = NULL;

//
//
// Init the configuration structure with default values
//
WINSCNF_CNF_T        WinsCnf = {
                                        WINSCNF_INITIAL_CNF_MAGIC_NO,        //id
NOTE("Change 1 to 0 before production")
                                        0,                //Log detailed evts
                    1,      //default number of processors
                    200,    //default no. of db buffers
                                        { 0, NULL},        //Spec.grp mask
                                        WINSCNF_E_INITING,        //state
                                        WINSCNF_DEF_REFRESH_INTERVAL,
                                        WINSCNF_MIN_TOMBSTONE_INTERVAL,
                                        WINSCNF_MIN_TOMBSTONE_TIMEOUT,
                                        WINSCNF_MIN_VERIFY_INTERVAL,
                                        WINSCNF_SCV_CHUNK,
                                        WINSCNF_DEF_CHL_MAX_RETRIES,
                                        WINSCNF_DEF_INIT_CHL_RETRY_INTVL,
                                        WINSCNF_DB_NAME_ASCII,  //db file name
                                        0,                //no of STATIC files
                                        NULL,                //ptr to file names
                                        DONT_DO_STATIC_INIT,
                                        (HANDLE)0,  //notify event handle (WINS)
                                        (HANDLE)0,  //not. evt hdl (PARAMETSRS)
                                        (HANDLE)0,  //not. evt hdl (PARTNERS)
                                        (HANDLE)0,  //Config change handle
                                        (HANDLE)0,  //log event handle
                                        (DWORD)WINSINTF_E_NORMAL,
                                        WINSTHD_DEF_NO_NBT_THDS,
                                        WINSCNF_SCV_PRIORITY_LVL,
                                        WINSCNF_MIN_VALID_RPL_INTVL,//max Rpl
                                                                    //Time Intvl
                                        TRUE,     //rpl. only with cnf partners
                                        TRUE,     //add 1B to responses to 1C name queries
#if MCAST > 0
                                        FALSE,    //no rpl. with self found pnrs
                                        WINSCNF_DEF_MCAST_TTL,
                                        WINSCNF_DEF_MCAST_INTVL,
#endif
                                        TRUE,     //logging is on
                                        NULL,     //current directory
                                        NULL,     //no backup directory
                                        FALSE,    //Do backup on term flg
                                        FALSE,    //PStatic flag
                                        0,        //type of persona list (0 = non-grata)
                                        0,        //number of addresses in persona list
                                        NULL,     //persona list
                                        WINSCNF_RPL_DEFAULT_TYPE, //def. rpl
                                        TRUE,    //No rpl on error
                                        TRUE,   //no persistent connections
                                //
                                // CC initialization
                                //
                                        MAXULONG, //CC Time Int
                                        FALSE,    //SpTime Set
                                        MAXULONG, //Sp Time
                                        WINSCNF_CC_DEF_RECS_AAT,
                                        WINSCNF_CC_DEF_USE_RPL_PNRS,

                                        FALSE,  //no spoofing
                                        FALSE,  // no randomization of 1C list.
                                //
                                //PullInfo initialization
                                //
                                          WINSCNF_MAX_COMM_RETRIES, //comm.
                                                                     //failure
                                                                     //retries
                                          0,   //no of Push Pnrs
                                          NULL,//ptr to Pull Pnrs records
                                          DO_INIT_TIME_RPL,  //do init time
                                                             //pulling
                                          WINSCNF_RPL_DEFAULT_TYPE,
                                          TRUE,  // persistent connections
                                //
                                // PushInfo initialization
                                //
                                          TRUE, // trigger on address change
                                                 //of owned entry
                                          0,   //no of Pull Pnrs
                                          0,   //no of Push recs with valid
                                               //update count
                                          NULL,//ptr to Push Pnrs records
                                          DO_INIT_TIME_RPL, //init time
                                                            //pushing disabled
                                          DO_PROP_NET_UPD_NTF,  //prop net upd
                                                               //ntfs.
                                          WINSCNF_RPL_DEFAULT_TYPE,
                                          TRUE  // persistent connections
                          };


/*
 *        Local Variable Definitions
*/
STATIC BOOL     sfVersNoUpdThdExists = FALSE;
STATIC BOOL     sfVersNoChanged = FALSE;

STATIC HKEY        sConfigRoot;              //HKEY for the WINS root
STATIC HKEY        sParametersKey;    //HKEY for the PARAMETERS subkey
STATIC HKEY        sCCKey;            //HKEY for the CC subkey
STATIC HKEY        sPartnersKey;      //HKEY for PARTNERS subkey
STATIC HKEY        sLogRoot;          //HKEY for the log root

FUTURES("Might want to change these to auto variables later")
STATIC TCHAR    sWinsCfgKey[]                 = _WINS_CFG_KEY;
STATIC TCHAR    sWinsLogKey[]                 = _WINS_LOG_KEY;
STATIC TCHAR    sWinsMsgFileSKey[]      = _WINS_MSGFILE_SKEY;

//
// flags that indicate to WinsCnfOpenSubKeys() whether the corresponding keys
// exist.
//
STATIC BOOL     sfParametersKeyExists = FALSE;
STATIC BOOL         sfPartnersKeyExists   = FALSE;

STATIC BOOL     sfParametersKeyOpen = FALSE;
STATIC BOOL         sfPartnersKeyOpen   = FALSE;

TCHAR        sLogFilePath[WINS_MAX_FILENAME_SZ];   //path to log file

/*
 *        Local Function Prototype Declarations
*/

/* prototypes for functions local to this module go here */




STATIC
VOID
LnkWSameMetricValRecs(
        PWINSCNF_CNF_T           pWinsCnf,
        PRPL_CONFIG_REC_T  pCnfRec
        );

STATIC
int
__cdecl
CompUpdCnt(
        CONST LPVOID  pElem1,
        CONST LPVOID  pElem2
        );

STATIC
VOID
GetPnrInfo(
        RPL_RR_TYPE_E   RRType_e,
        PWINSCNF_CNF_T  pWinsCnf
        );

STATIC
VOID
GetKeyInfo(
        IN  HKEY                   Key,
        IN  WINSCNF_KEY_E        KeyTyp_e,
        OUT LPDWORD                  pNoOfSubKeys,
        OUT LPDWORD                pNoOfVals
        );

STATIC
BOOL
SanityChkParam(
        PWINSCNF_CNF_T  pWinsCnf
        );

STATIC
VOID
ChkWinsSubKeys(
        VOID
        );

STATIC
VOID
GetSpTimeData(
        HKEY              SubKey,
        LPSYSTEMTIME      pCurrTime,
        LPBOOL            pfSpTime,
        LPDWORD           pSpTimeIntvl
        );
STATIC
VOID
ReadSpecGrpMasks(
        PWINSCNF_CNF_T pWinsCnf
        );

VOID
GetOwnerList(
  PWINSCNF_CNF_T  pWinsCnf
 );

VOID
ReadCCInfo(
 PWINSCNF_CNF_T  pWinsCnf
);

#if MCAST > 0
STATIC
DWORD
SetVal(
       HKEY     RootKey,
       LPWSTR   pName,
       DWORD    ValType,
       LPWSTR   pVal,
       DWORD    ValSize
);
#endif

#ifdef WINSDBG
STATIC
VOID
PrintRecs(
        RPL_RR_TYPE_E  RRType_e,
        PWINSCNF_CNF_T  pWinsCnf
        );
#endif

/*function defs*/

STATUS
WinsCnfInitConfig(
        VOID
        )
/*++

Routine Description:

        This function opens the registry and reads in all the configuration
        information from it.


Arguments:
        None

Externals Used:
        WinsCnf

Called by:
        Init() in nms.c

Comments:
        None

Return Value:

   Success status codes --
   Error status codes  --

--*/

{
   DWORD  NewKeyInd;
   LONG          RetVal;

   /*
        First and foremost, open (or create if non-existent) the log file
   */
#if 0
   InitLog();
#endif

   RetVal = RegCreateKeyEx(
                HKEY_LOCAL_MACHINE,        //predefined key value
                sWinsCfgKey,                //subkey for WINS
                0,                        //must be zero (reserved)
                TEXT("Class"),                //class -- may change in future
                REG_OPTION_NON_VOLATILE, //non-volatile information
                KEY_ALL_ACCESS,                //we desire all access to the keyo
                NULL,                         //let key have default sec. attributes
                &sConfigRoot,                //handle to key
                &NewKeyInd                //is it a new key (out arg)
                );



    if (RetVal != ERROR_SUCCESS)
    {
        WINSEVT_LOG_N_RET_M(
                        WINS_FATAL_ERR,
                        WINS_EVT_CANT_OPEN_WINS_KEY,
                        WINS_FATAL_ERR
                           );
    }

   //
   // Initialize the critical section that guards the fields used
   // by Scavenger thread
   //
   InitializeCriticalSection(&WinsCnfCnfCrtSec);
   InitializeCriticalSection(&g_cs1BFilter);

   /*
        First create the events that will be passed to the
        RegNotifyChangeKeyValue function
   */
try {
   WinsMscCreateEvt(
                        WINS_KEY_CHG_EVT_NM,
                        FALSE,        //auto reset event
                        &WinsCnf.WinsKChgEvtHdl
                      );
   WinsMscCreateEvt(
                        PARAMETERS_KEY_CHG_EVT_NM,
                        FALSE,        //auto reset event
                        &WinsCnf.ParametersKChgEvtHdl
                      );
   WinsMscCreateEvt(
                        PARTNERS_KEY_CHG_EVT_NM,
                        FALSE,        //auto reset event
                        &WinsCnf.PartnersKChgEvtHdl
                      );

}
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsCnfInitConfig");
        WINSEVT_LOG_M(WINS_FATAL_ERR, WINS_EVT_CANT_CREATE_REG_EVT);
        return(WINS_FAILURE);
}

   //
   // Create the event that this main thread will set when configuration changes
   // The main thread sets this event to notify the scavenger thread (for now)
   // about the changes
   //
   WinsMscCreateEvt(
                        CNF_CHG_EVT_NM,
                        FALSE,        //auto reset event
                        &WinsCnf.CnfChgEvtHdl
                      );
   //
   // Opens the Partners and Parameters keys
   //
   WinsCnfOpenSubKeys();

   //
   // Read in the registry information
   //
   WinsCnfReadRegInfo(&WinsCnf);


   /*
        Ask to be notified when the Configuration key or any of the subkeys
        change
   */
    WinsCnfAskToBeNotified(WINSCNF_E_WINS_KEY);
    return(WINS_SUCCESS);
}

VOID
WinsCnfReadPartnerInfo(
        PWINSCNF_CNF_T pWinsCnf
        )

/*++

Routine Description:

  This function gets all the information pertaining to the Partners of this
  WINS. Under the configuration key above, there are two Keys PULL and PUSH.
  Under each key, there can be one or more keys (IP addresses). The values
  for each IP address key are:
        Time Interval   (for both Pull and Push IP address keys)
        Update Count        (for Push IP address keys)


Arguments:
        pWinsCnf - Address of Wins Configuration structure

Externals Used:
        None


Return Value:
        None

Error Handling:

Called by:
   Init function of the Replicator.

Side Effects:

Comments:

    Note: This function should never be called when inside the
    NmsNmhNamRegCrtSec, otherwise a deadlock can occur with the Pull
    thread (check out Reconfig in rplpull.c)
--*/

{

  DWORD                 ValTyp;
  //
  // Initialize the MaxRplTimeInterval field to 0. After we have read
  // both the PULL and PUSH key information from the registry, the above
  // field will contain the max. replication time interval specified for
  // pulling and pushing replicas
  //
  pWinsCnf->MaxRplTimeInterval = 0;

  pWinsCnf->PullInfo.NoOfPushPnrs   = 0;
  pWinsCnf->PullInfo.pPullCnfRecs   = NULL;
  pWinsCnf->PullInfo.RplType        = WINSCNF_RPL_DEFAULT_TYPE;

  pWinsCnf->PushInfo.NoOfPullPnrs   = 0;
  pWinsCnf->PushInfo.pPushCnfRecs   = NULL;
  pWinsCnf->PushInfo.fAddChgTrigger = FALSE;
  pWinsCnf->PushInfo.RplType        = WINSCNF_RPL_DEFAULT_TYPE;
  //
  // Since we are again reading the info about the Partners key, increment
  // the magic no.  No other thread increments this no. The thread that
  // looks at this no is the Pull thread.
  //
  EnterCriticalSection(&WinsCnfCnfCrtSec);
  pWinsCnf->MagicNo = ++WinsCnfCnfMagicNo;
  LeaveCriticalSection(&WinsCnfCnfCrtSec);

try {
  GetOwnerList(pWinsCnf);    // get list of persona (grata / non-grata)
  //
  // Read in the RplType DWORD.  Even if no partners are defined, we do this
  // in case this WINS is open to all partners (i.e. fOnlyWCnfPnrs is FALSE)
  //
  QUERY_VALUE_M(
                          sPartnersKey,
                          WINSCNF_RPL_TYPE_NM,
                          ValTyp,
                          pWinsCnf->RplType,
                          0,      // no logging
                          WINSCNF_RPL_DEFAULT_TYPE
                            );

  GetPnrInfo(RPL_E_PULL, pWinsCnf);
  GetPnrInfo(RPL_E_PUSH, pWinsCnf);
}
except(EXCEPTION_EXECUTE_HANDLER) {
        DWORD ExcCode = GetExceptionCode();
        //
        // If there is some problem with the registry, we don't want
        // to bugcheck WINS.  It can proceed with default values.
        // Since we have already logged the errors, the administrator
        // can take corrective action if necessary
        //
        if (
                (ExcCode != WINS_EXC_CANT_OPEN_KEY)
                         &&
                (ExcCode != WINS_EXC_CANT_QUERY_KEY)
                        &&
                (ExcCode != WINS_EXC_CANT_CLOSE_KEY)
           )
        {
                WINS_RERAISE_EXC_M();
        }
}
  return;
}



STATUS
WinsCnfInitLog(
        VOID
        )

/*++

Routine Description:
        This function open (or creates) a log file for registering events

Arguments:
        None

Externals Used:
        None


Return Value:

   Success status codes --  WINS_SUCCESS
   Error status codes   --  WINS_FAILURE

Error Handling:

Called by:
        WinsCnfInitConfig

Side Effects:

Comments:
        None
--*/
{

   LONG            RetVal = ERROR_SUCCESS;
   STATUS   RetStat = WINS_SUCCESS;

#ifdef WINS_INTERACTIVE
   DWORD    NewKeyInd;
   TCHAR    Buff[160];
   DWORD    dwData;

   RetVal =  RegCreateKeyEx(
                HKEY_LOCAL_MACHINE,        //predefined key value
                sWinsLogKey,                //subkey for WINS
                0,                        //must be zero (reserved)
                TEXT("Class"),                //class -- may change in future
                REG_OPTION_NON_VOLATILE, //non-volatile information
                KEY_ALL_ACCESS,                //we desire all access to the keyo
                NULL,                         //let key have default sec. attributes
                &sLogRoot,                //handle to key
                &NewKeyInd                //is it a new key (out arg) -- not
                                        //looked at
                );


   if (RetVal != ERROR_SUCCESS)
   {
        return(WINS_FAILURE);
   }


   /*
        Set the event id message file name
   */
   lstrcpy(Buff, _WINS_LOG_FILE_NAME);

   /*
       Add the Event-ID message-file name to the subkey
   */
   RetVal = RegSetValueEx(
                        sLogRoot,            //key handle
                        sWinsMsgFileSKey,   //value name
                        0,                    //must be zero
                        REG_EXPAND_SZ,            //value type
                        (LPBYTE)Buff,
                        (lstrlen(Buff) + 1) * sizeof(TCHAR)   //length of value data
                         );

   if (RetVal != ERROR_SUCCESS)
   {
        return(WINS_FAILURE);
   }

   /*
     Set the supported data types flags
   */
   dwData = EVENTLOG_ERROR_TYPE       |
            EVENTLOG_WARNING_TYPE     |
            EVENTLOG_INFORMATION_TYPE;


   RetVal = RegSetValueEx (
                        sLogRoot,            //subkey handle
                        TEXT("TypesSupported"),  //value name
                        0,                    //must be zero
                        REG_DWORD,            //value type
                        (LPBYTE)&dwData,    //Address of value data
                        sizeof(DWORD)            //length of value data
                          );

   if (RetVal != ERROR_SUCCESS)
   {
        return(WINS_FAILURE);
   }

   /*
    * Done with the key.  Close it
   */
   RetVal = RegCloseKey(sLogRoot);

   if (RetVal != ERROR_SUCCESS)
   {
        return(WINS_FAILURE);
   }
#endif
   WinsCnf.LogHdl = RegisterEventSource(
                                (LPCTSTR)NULL,         //use local machine
                                TEXT("Wins")
                                      );
   if (WinsCnf.LogHdl == NULL)
   {
        DBGPRINT1(ERR, "InitLog: RegisterEventSource error = (%x)\n", GetLastError());
        return(WINS_FAILURE);
   }

   WINSEVT_LOG_INFO_D_M(WINS_SUCCESS, WINS_EVT_LOG_INITED);
   return(RetStat);
}

VOID
LnkWSameMetricValRecs(
        PWINSCNF_CNF_T           pWinsCnf,
        PRPL_CONFIG_REC_T  pCnfRec
        )

/*++

Routine Description:
        This function is called to link a configuration record with all
        other configuration records with the same metric value.  The metric
        to use depends upon the type of the record.  If it is a PULL record,
        the metric is "Time Interval".  If the record is a PUSH record,
        the metric is "Update Count"


Arguments:
        pWinsCnf - Address of configuration block
        pCnfRec - Configuration Record to link.

Externals Used:
        None


Return Value:
        None

Error Handling:

Called by:

        WinsCnfReadPartnerInfo

Side Effects:

Comments:
        The record to be linked is the last record in the buffer of
        records of the same type.
--*/

{
        PRPL_CONFIG_REC_T        pTmp;
        DWORD                        OffMetricToComp;
        LONG                        MetricVal;

        //
        // Set the variables used later based on the record type
        //
        if (pCnfRec->RRTyp_e == RPL_E_PULL)
        {
                pTmp            = pWinsCnf->PullInfo.pPullCnfRecs;
                MetricVal       = pCnfRec->TimeInterval;
                OffMetricToComp = offsetof(RPL_CONFIG_REC_T, TimeInterval);
        }
        else  //it is a PUSH record
        {
                pTmp            = pWinsCnf->PushInfo.pPushCnfRecs;
                MetricVal       = pCnfRec->UpdateCount;
                OffMetricToComp = offsetof(RPL_CONFIG_REC_T, UpdateCount);
        }

        //
        // Link in this record at the end of the linked list of
        // records with the same metric value in the buffer pointed by
        // the starting value of pTmp (set above).
        //
        for (
                ;
                pTmp != pCnfRec;                //until we reach this record
                pTmp = (PRPL_CONFIG_REC_T)((LPBYTE)pTmp + RPL_CONFIG_REC_SIZE)
            )
         {
                //
                // If Metric Value is same, go to end of linked list and
                // link in the record
                //
                if (*((LONG *)((LPBYTE)pTmp + OffMetricToComp)) == MetricVal)
                {
                        //
                        // Note: if the metric is UpdateCount (Push records)
                        // then, the following if will fail.
                        //

                        //
                        // If both records have a specific time for replication,
                        // that time must agree too
                        //
                        if (pTmp->fSpTime &&  pCnfRec->fSpTime)
                        {
                                //
                                // If specific time is not the same, go to the
                                // next record in the array
                                //
                                if (pTmp->SpTimeIntvl != pCnfRec->SpTimeIntvl)
                                {
                                        continue;
                                }
                        }

                        for(
                                ;
                                pTmp->pNext != NULL;
                                pTmp = pTmp->pNext
                           )
                                ;        //NULL body

                        pTmp->pNext            = pCnfRec;

                        //
                        // Set flag to indicate that this record has
                        // been linked. Used in SubmitTimerReqs in rplpull.c
                        //
                        pCnfRec->fLinked = TRUE;
                        break;  //record is linked. break out of the loop
                }

        }  //end of for { .. } for looping over all records in the buffer

        //
        // Make pNext to NULL since this is the last record in the buffer
        // buffer of Config Records (also in the chain if records with
        // the same metric)
        //
        pCnfRec->pNext = NULL;
        return;
}





VOID
WinsCnfSetLastUpdCnt(
        PWINSCNF_CNF_T        pWinsCnf
        )

/*++

Routine Description:

        This function is called at initialization/reinitialization time if
        InitTimePush registry variable is set to 1) to set the LastVersNo
        field of all Push Configuration records to the value of the
        NmsNmhMyMAxVersNo counter.  This is done to avoid Push Notifications
        to be sent at Init time.

Arguments:
        pWinsCnf - Wins Configuration Info

Externals Used:
        NmsNmhMyMaxVersNo

Return Value:
        None

Error Handling:

Called by:
        NmsDbInit, Reinit (in nms.c)

Side Effects:

Comments:
        This function is called only after the local Database
        Name-Address mapping table has been read and NmsNmhMyMaxVersNo
        counter initialized (see GetMaxVersNos in nmsdb.c).  Also,
        this function is called only if the counter value is > 0
--*/

{
        PRPL_CONFIG_REC_T pCnfRec = pWinsCnf->PushInfo.pPushCnfRecs;

        for (
                ;   //null expr 1
                pCnfRec->WinsAdd.Add.IPAdd != INADDR_NONE;
                pCnfRec = (PRPL_CONFIG_REC_T)(
                               (LPBYTE) pCnfRec + RPL_CONFIG_REC_SIZE
                                             )
            )
        {
                //
                // If the Update count field is invalid, go to the next record
                //
                if (pCnfRec->UpdateCount == RPL_INVALID_METRIC)
                {
                        continue;
                }

                pCnfRec->LastVersNo = NmsNmhMyMaxVersNo;
        }

        return;
}

VOID
GetPnrInfo(
        RPL_RR_TYPE_E  RRType_e,
        PWINSCNF_CNF_T  pWinsCnf
        )

/*++

Routine Description:
        This function is called to read PULL/PUSH records

Arguments:
        RRType_e - Type of Information to read (PULL or PUSH records)
        pWinsCnf  - Configuration structure


Externals Used:
        None


Return Value:
        None

Error Handling:

Called by:
        WinsCnfReadPartnerInfo

Side Effects:

Comments:
        None
--*/

{

  LONG                  RetVal;
  HKEY                  CnfKey;
  TCHAR                 KeyName[20]; // will hold name of subkey of
                                     // PULL/PUSH records. These keys are IP
                                     // addresses for which 20 is a
                                     // big enough size

  CHAR                  AscKeyName[20];
  DWORD                 KeyNameSz;
  FILETIME              LastWrite;
  DWORD                 BuffSize;
  HKEY                  SubKey;
  DWORD                 ValTyp;
  DWORD                 Sz;
  PRPL_CONFIG_REC_T     paCnfRecs;
  DWORD                 NoOfPnrs   = 0;    //# of valid PULL or PUSH pnrs
  DWORD                 NoOfPnrsSv;        //# of valid PULL or PUSH pnrs saved
  DWORD                 NoOfVals;
  DWORD                 InitTime;
  DWORD                 IndexOfPnr = 0;   //total # of pnrs
  DWORD                 RplType;
  SYSTEMTIME            CurrTime;

  //
  // Get the current time.  It may be needed if we have partners with SpTime
  // specified.
  //
  if (RRType_e == RPL_E_PULL)
  {
        GetLocalTime(&CurrTime);
  }

   /*
   *  Open the key (PULL/PUSH)
   */
   RetVal =   RegOpenKeyEx(
                sConfigRoot,                //predefined key value
                RRType_e == RPL_E_PULL ?
                        _WINS_CFG_PULL_KEY :
                        _WINS_CFG_PUSH_KEY,        //subkey for WINS
                0,                        //must be zero (reserved)
                KEY_READ,                //we desire read access to the keyo
                &CnfKey                        //handle to key
                );

   if (RetVal != ERROR_SUCCESS)
   {

CHECK("Is there any need to log this")
        WINSEVT_LOG_INFO_M(
                                WINS_SUCCESS,
                                RRType_e == RPL_E_PULL ?
                                        WINS_EVT_CANT_OPEN_PULL_KEY :
                                        WINS_EVT_CANT_OPEN_PUSH_KEY
                         );
   }
   else   //key was successfully opened
   {

        /*
        *        Query the key.  The subkeys are IP addresses of PULL
        *      partners.
        */
        GetKeyInfo(
                        CnfKey,
                        (RRType_e == RPL_E_PULL ? WINSCNF_E_PULL_KEY :
                                                WINSCNF_E_PUSH_KEY),
                        &NoOfPnrs,
                        &NoOfVals   //ignored
                      );

        if (NoOfPnrs == 0)
        {

             WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                RRType_e == RPL_E_PULL ?
                                        WINS_EVT_NO_SUBKEYS_UNDER_PULL  :
                                        WINS_EVT_NO_SUBKEYS_UNDER_PUSH
                                );
        }
        else
        {

                //
                // Since we have one or more Partners to replicate with,
                // read in the value of the InitTimeReplication attribute
                // of all such Partners
                //
                QUERY_VALUE_M(
                                CnfKey,
                                WINSCNF_INIT_TIME_RPL_NM,
                                ValTyp,
                                InitTime,
                                0, //WINS_EVT_CANT_GET_INITRPL_VAL,
                                DO_INIT_TIME_RPL
                             );

                //
                // Since we have one or more Partners to replicate with,
                // read in the value of the RplType attribute
                //
                QUERY_VALUE_M(
                                CnfKey,
                                WINSCNF_RPL_TYPE_NM,
                                ValTyp,
                                RplType,
                                0,
                                pWinsCnf->RplType
                             );
#if PRSCONN
               QUERY_VALUE_M(
                                   CnfKey,
                                   WINSCNF_PRS_CONN_NM,
                                   ValTyp,
                                   pWinsCnf->fPrsConn,
                                   0,
                                   TRUE
                                );
#endif
                //
                // Allocate buffer big enough to hold data for
                // the number of subkeys found under the PULL key
                //
                BuffSize = RPL_CONFIG_REC_SIZE * (NoOfPnrs + 1);
                WinsMscAlloc( BuffSize, &paCnfRecs);

                if (RRType_e == RPL_E_PULL)
                {
                        pWinsCnf->PullInfo.pPullCnfRecs = paCnfRecs;
                        QUERY_VALUE_M(
                                CnfKey,
                                WINSCNF_RETRY_COUNT_NM,
                                ValTyp,
                                pWinsCnf->PullInfo.MaxNoOfRetries,
                                0, //WINS_EVT_CANT_GET_RETRY_COUNT,
                                WINSCNF_MAX_COMM_RETRIES
                             );
                }
                else
                {
                        //
                        // Get the value of the field that indicates
                        // whether we should send a trigger when the
                        // address of an owned entry changes.
                        //
                        Sz = sizeof(pWinsCnf->PushInfo.fAddChgTrigger);
                        RetVal = RegQueryValueEx(
                                     CnfKey,
                                     WINSCNF_ADDCHG_TRIGGER_NM,
                                     NULL,        //reserved; must be NULL
                                     &ValTyp,
                                     (LPBYTE)&pWinsCnf->PushInfo.fAddChgTrigger,
                                     &Sz
                                                );

                        if (RetVal != ERROR_SUCCESS)
                        {
                            pWinsCnf->PushInfo.fAddChgTrigger = FALSE;
                        }
                        else
                        {
                            pWinsCnf->PushInfo.fAddChgTrigger =
                                (pWinsCnf->PushInfo.fAddChgTrigger >= 1);

                        }

                        QUERY_VALUE_M(
                                CnfKey,
                                WINSCNF_PROP_NET_UPD_NTF,
                                ValTyp,
                                pWinsCnf->PushInfo.PropNetUpdNtf,
                                0,                    //no event
                                DO_PROP_NET_UPD_NTF
                             );

                        pWinsCnf->PushInfo.pPushCnfRecs = paCnfRecs;
                        pWinsCnf->PushInfo.NoPushRecsWValUpdCnt = 0;
                }

                /*
                *   For each key, get the values (Time Interval/UpdateCount,
                *   etc)
                */
                NoOfPnrsSv = NoOfPnrs;  //save the number that we got from the
                                        //GetkeyInfo function
                for(
                     IndexOfPnr = 0, NoOfPnrs = 0;
                     NoOfPnrs < NoOfPnrsSv;  //no of valid pnrs < the total #
                     IndexOfPnr++
                   )
                {
                        KeyNameSz = sizeof(KeyName)/sizeof(TCHAR);  //init before every call
                        RetVal = RegEnumKeyEx(
                                CnfKey,
                                IndexOfPnr,       //Index Of Pnr
                                KeyName,
                                &KeyNameSz,
                                NULL,           //reserved
                                NULL,           //don't need class name
                                NULL,           //ptr to var. to hold class name
                                &LastWrite      //not looked at by us
                                );

                        if (RetVal != ERROR_SUCCESS)
                        {
                                //
                                // No more ip address keys to get
                                //
                                break;
                        }

                        //
                        // Store pointer to the Wins Config structure in
                        // the configuration record
                        //
                        paCnfRecs->pWinsCnf = pWinsCnf;

                        //
                        // pWinsCnf->MagicNo contains the value of
                        // WinsCnfCnfMagicNo
                        //
                        paCnfRecs->MagicNo  = pWinsCnf->MagicNo;
                        paCnfRecs->RRTyp_e  = RRType_e;

#ifdef UNICODE
                        if (wcstombs(AscKeyName, KeyName, KeyNameSz) == -1)
                        {
                                DBGPRINT0(ERR,
                           "Conversion not possible in the current locale\n");
                        }
                        AscKeyName[KeyNameSz] = EOS;

NONPORT("Call a comm function to do this")
                        paCnfRecs->WinsAdd.Add.IPAdd = inet_addr(AscKeyName);
#else
                        paCnfRecs->WinsAdd.Add.IPAdd = inet_addr(KeyName);
#endif

                        //
                        // inet_addr returns bytes in network byte order
                        // (Left to Right).  Let us convert this into host
                        // order.  This will avoid confusion later on. All
                        // formatting functions expect address to be in host
                        // order.
                        //
                        paCnfRecs->WinsAdd.AddLen = COMM_IP_ADD_SIZE;
                        paCnfRecs->WinsAdd.AddTyp_e = COMM_ADD_E_TCPUDPIP;
                        paCnfRecs->WinsAdd.Add.IPAdd = ntohl(
                                             paCnfRecs->WinsAdd.Add.IPAdd
                                                    );
                        if (COMM_ADDRESS_SAME_M(&NmsLocalAdd, &paCnfRecs->WinsAdd))
                        {
                                //
                                // Invalid partner. Ignore. NoOfPnrs will
                                // not be incremented.  Also, the buffer
                                // pointer stays the same
                                //
                                continue;
                        }
                        RetVal = RegOpenKeyEx(
                                                CnfKey,
                                                KeyName,
                                                0,        //reserved; must be 0
                                                KEY_READ,
                                                &SubKey
                                                    );

                        if (RetVal != ERROR_SUCCESS)
                        {
                                WINSEVT_LOG_M(
                                        WINS_FATAL_ERR,
                                        RRType_e == RPL_E_PULL ?
                                                WINS_EVT_CANT_OPEN_PULL_SUBKEY :
                                                WINS_EVT_CANT_OPEN_PUSH_SUBKEY
                                             );
FUTURES("It is possible that the user deleted the key. Recover from this")
                               if (RRType_e == RPL_E_PULL)
                               {
                                pWinsCnf->PullInfo.NoOfPushPnrs = 0;
                                WinsMscDealloc(pWinsCnf->PullInfo.pPullCnfRecs);
                                pWinsCnf->PullInfo.pPullCnfRecs = NULL;
                               }
                               else
                               {
                                pWinsCnf->PushInfo.NoOfPullPnrs = 0;
                                WinsMscDealloc(pWinsCnf->PushInfo.pPushCnfRecs);
                                pWinsCnf->PushInfo.pPushCnfRecs = NULL;
                               }
                               WINS_RAISE_EXC_M(WINS_EXC_CANT_OPEN_KEY);
                        }

FUTURES("Maybe, we will support a time interval attribute for Push records")
FUTURES("when that is done, LnkRecsWSameMetric would need to be updated")

                        if (RRType_e == RPL_E_PULL)
                        {

                           //
                           // Read in specific time for replication if one
                           // has been specified
                           //
                           GetSpTimeData(SubKey, &CurrTime, &paCnfRecs->fSpTime, &paCnfRecs->SpTimeIntvl);

                           Sz = sizeof(paCnfRecs->TimeInterval);
                           RetVal = RegQueryValueEx(
                                               SubKey,
                                               WINSCNF_RPL_INTERVAL_NM,
                                               NULL,        //reserved; must be NULL
                                               &ValTyp,
                                               (LPBYTE)&paCnfRecs->TimeInterval,
                                               &Sz
                                                );

                           if (RetVal != ERROR_SUCCESS)
                           {
                                WINSEVT_LOG_INFO_D_M(
                                        WINS_SUCCESS,
                                        WINS_EVT_CANT_GET_PULL_TIMEINT
                                                    );
                                paCnfRecs->TimeInterval = RPL_INVALID_METRIC;
                           }
                           else  // a value was read in
                           {
                                //
                                // If the time interval is less than or
                                // equal to the minimum allowed, use the
                                // default minimum
                                //
                                if (paCnfRecs->TimeInterval
                                        < WINSCNF_MIN_VALID_RPL_INTVL)
                                {
                                        paCnfRecs->TimeInterval =
                                                WINSCNF_MIN_VALID_RPL_INTVL;
                                }
                                if (
                                   (DWORD)paCnfRecs->TimeInterval >
                                                 pWinsCnf->MaxRplTimeInterval
                                   )
                                {
                                        pWinsCnf->MaxRplTimeInterval =
                                                       paCnfRecs->TimeInterval;
                                }
                           }

                           //
                           // Read in the precedence level.  This can currently
                           // be either HIGH (> 0) or LOW (0).
                           //
                           Sz = sizeof(paCnfRecs->MemberPrec);
                           RetVal = RegQueryValueEx(
                                               SubKey,
                                               WINSCNF_MEMBER_PREC_NM,
                                               NULL,   //reserved; must be NULL
                                               &ValTyp,
                                               (LPBYTE)&paCnfRecs->MemberPrec,
                                               &Sz
                                                );
                           if (RetVal != ERROR_SUCCESS)
                           {
                                paCnfRecs->MemberPrec =  WINSCNF_LOW_PREC;
                           }
                           else
                           {
                                paCnfRecs->MemberPrec =
                                  (paCnfRecs->MemberPrec > 0) ?
                                         WINSCNF_HIGH_PREC : WINSCNF_LOW_PREC;
                           }

#if PRSCONN
                           QUERY_VALUE_M(
                                SubKey,
                                WINSCNF_PRS_CONN_NM,
                                ValTyp,
                                paCnfRecs->fPrsConn,
                                0,
                                pWinsCnf->fPrsConn
                             );
#endif
                        }
                        else  // it is a PUSH record
                        {

                                //
                                // Currently, we don't support periodic
                                // or specific time replication for Push
                                // records
                                //
                                paCnfRecs->fSpTime = FALSE;

#if PRSCONN
                                QUERY_VALUE_M(
                                   SubKey,
                                   WINSCNF_PRS_CONN_NM,
                                   ValTyp,
                                   paCnfRecs->fPrsConn,
                                   0,
                                   pWinsCnf->fPrsConn
                                );
#endif

                                Sz = sizeof(paCnfRecs->UpdateCount);
                                RetVal = RegQueryValueEx(
                                                SubKey,
                                                WINSCNF_UPDATE_COUNT_NM,
                                                NULL,
                                                &ValTyp,
                                                (LPBYTE)&paCnfRecs->UpdateCount,
                                                &Sz
                                                        );

                                if (RetVal != ERROR_SUCCESS)
                                {
                                        paCnfRecs->UpdateCount =
                                                        RPL_INVALID_METRIC;
                                }
                                else
                                {
                                        paCnfRecs->LastVersNo.QuadPart = 0;
#if PRSCONN
                                        if (!paCnfRecs->fPrsConn && !sfNoLimitChk)
#else
                                        if (!sfNoLimitChk)
#endif
                                        {
                                          if (paCnfRecs->UpdateCount <
                                                 WINSCNF_MIN_VALID_UPDATE_CNT)
                                          {
                                                paCnfRecs->UpdateCount =
                                                   WINSCNF_MIN_VALID_UPDATE_CNT;
                                          }
                                        }
                                        else
                                        {
                                             if (paCnfRecs->UpdateCount == 0)
                                             {
                                                 paCnfRecs->UpdateCount = 1;
                                             }
                                        }
                                        pWinsCnf->PushInfo.NoPushRecsWValUpdCnt++;
                                }



                        }


#if MCAST > 0
                        Sz = sizeof(paCnfRecs->fSelfFnd);
                        RetVal = RegQueryValueEx(
                                                SubKey,
                                                WINSCNF_SELF_FND_NM,
                                                NULL,
                                                &ValTyp,
                                                (LPBYTE)&paCnfRecs->fSelfFnd,
                                                &Sz
                                                        );

                        if (RetVal != ERROR_SUCCESS)
                        {
                                paCnfRecs->fSelfFnd = FALSE;
                        }
#endif

                        Sz = sizeof(paCnfRecs->fOnlyDynRecs);
                        RetVal = RegQueryValueEx(
                                                SubKey,
                                                WINSCNF_ONLY_DYN_RECS_NM,
                                                NULL,
                                                &ValTyp,
                                                (LPBYTE)&paCnfRecs->fOnlyDynRecs,
                                                &Sz
                                                        );

                        if (RetVal != ERROR_SUCCESS)
                        {
                                paCnfRecs->fOnlyDynRecs = FALSE;
                        }

                        QUERY_VALUE_M(
                                SubKey,
                                WINSCNF_RPL_TYPE_NM,
                                ValTyp,
                                paCnfRecs->RplType,
                                0,
                                pWinsCnf->RplType
                             );


                        if (paCnfRecs->RplType != 0)
                        {
                          WINSEVT_LOG_INFO_M(paCnfRecs->WinsAdd.Add.IPAdd, WINS_EVT_PARTIAL_RPL_TYPE);
                        }

#if PRSCONN
                        if (paCnfRecs->fPrsConn != 0)
                        {
                           paCnfRecs->fPrsConn = TRUE;
                        }
#endif

                        REG_M(
                                RegCloseKey(SubKey),
                                WINS_EVT_CANT_CLOSE_KEY,
                                WINS_EXC_CANT_CLOSE_KEY
                             );

                        //
                        // Initialize the retry count to 0 and the fLinked flag
                        // to FALSE
                        //
                        //used when pulling
                        //
                        paCnfRecs->RetryCount              = 0;

                        paCnfRecs->fLinked              = FALSE;

                        //
                        // Initialize the following to 0 so that once we stop
                        // communicating with a WINS we can start again when
                        // the following count reaches
                        // WINSCNF_RETRY_AFTER_THIS_MANY_RPL
                        //
                        paCnfRecs->RetryAfterThisManyRpl = 0;


#if PRSCONN
                        ECOMM_INIT_DLG_HDL_M(&paCnfRecs->PrsDlgHdl);
                        paCnfRecs->LastCommTime = 0;
#endif

                        //
                        // Initialize LastCommFailTime to 0. Used by
                        // SndPushNtf in rplpull.c
                        //

                        paCnfRecs->LastCommFailTime = 0;
                        paCnfRecs->PushNtfTries   = 0;

                        //
                        // Link the record with other PULL records with the same
                        // Time Interval
                        //
                        LnkWSameMetricValRecs(pWinsCnf, paCnfRecs);
                        //
                        // Mark the record as permanent (i.e. it will stay
                        // around until a reconfiguration or until the process
                        // terminates
                        //
                        paCnfRecs->fTemp = FALSE;

                        NoOfPnrs++;
                        paCnfRecs = (PRPL_CONFIG_REC_T)(
                                        (LPBYTE)paCnfRecs +
                                                RPL_CONFIG_REC_SIZE);
                } // end of for {..} for looping over subkeys of PULL

                //
                // GetReplicasNew expects the list to be terminated with a
                // record with INADDR_NONE as the address
                //
                paCnfRecs->WinsAdd.Add.IPAdd = INADDR_NONE;
                if (RRType_e == RPL_E_PULL)
                {
                      pWinsCnf->PullInfo.NoOfPushPnrs = NoOfPnrs;
                      pWinsCnf->PullInfo.InitTimeRpl  = InitTime;
                      pWinsCnf->PullInfo.RplType      = RplType;
                }
                else
                {
                      pWinsCnf->PushInfo.NoOfPullPnrs = NoOfPnrs;
                      pWinsCnf->PushInfo.InitTimePush = InitTime;
                      pWinsCnf->PushInfo.RplType      = RplType;

                      //
                      // Now that we are done with the Push record list,
                      //let us  sort it on the update count field
                      //
                      //
                      // Sort the array in increasing order of Update Counts
                      //

FUTURES("May use qsort to optimize the update notification process")
CHECK("Not sure yet whether sorting would optimize it")
#if 0
CHECK("this is resulting in compilation warnings.  haven't figured out")
CHECK("yet why.")
                      qsort(
                                pWinsCnf->pPushCnfRecs,        //start of array
                                (size_t)pWinsCnf->NoOfPullPnrs,//no of elements
                                RPL_CONFIG_REC_SIZE,        //size of each
                                                            //element
                                CompUpdCnt                    //compare func
                             );
#endif

                 } //end of else (It is PULL key)
            } // end of else (NoOfPnrs == 0)

            /*
             * Close the  key
            */
            REG_M(
                RegCloseKey(CnfKey),
                WINS_EVT_CANT_CLOSE_KEY,
                WINS_EXC_CANT_CLOSE_KEY
                      );
   } //end of else  (key could not be opened)
#if 0
#ifdef WINSDBG
     PrintRecs(RRType_e, pWinsCnf);
#endif
#endif

     return;
} // GetPnrInfo


VOID
GetOwnerList(
  PWINSCNF_CNF_T  pWinsCnf
 )

/*++

Routine Description:
  This function reads the list of owners whose records should be or should not be pulled
  from a partner WINS.


Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{
    LONG        RetVal;
    DWORD       dwValType;    // type of the reg value
    LPSTR       pValName;     // pointer to the reg 
    LPBYTE      pValData;     // pointer to the reg value's data
    DWORD       dwValDataLen; // length of the reg value's data

    DBGENTER("GetOwnerList\n");

    // query for the type of persona (grata (1) / non-grata (0))
    pWinsCnf->fPersonaGrata = 0;
    dwValDataLen = sizeof(DWORD);
    RetVal = RegQueryValueExA(
                sPartnersKey,                       // reg key [HKLM\System\CCS\Services\Wins\Partners]
                WINSCNF_PERSONA_MODE_NM,            // name of the value: "PersonaType"
                NULL,                               // reserved; must be NULL
                &dwValType,                         // type of the value: should get REG_DWORD
                (LPVOID)&(pWinsCnf->fPersonaGrata), // value data
                &dwValDataLen);                     // size of the value's data
    // if this call didn't succeed, we go on with the default which is 0, 'non-grata'

    // get the actual entry we're going to pick the list of addresses from
    pValName = pWinsCnf->fPersonaGrata ? 
                WINSCNF_PERSONA_GRATA_NM : 
                WINSCNF_PERSONA_NON_GRATA_NM;

    // get the size of the data from the registry
    // since Sz is 0, if there are any personas
    // grata/non-grata then we should get ERROR_MORE_DATA
    // if we get a different error than just remove the current list
    dwValDataLen = 0;
    RetVal = RegQueryValueExA(
                sPartnersKey,           // reg key [HKLM\System\CCS\Services\Wins\Partners]
                pValName,               // name of the value: "PersonaList"
                NULL,                   // reserved; must be NULL
                &dwValType,             // type of the value: should get REG_MULTI_SZ
                (LPVOID)&pValData,      // dummy address
                &dwValDataLen);         // initially 0 since we try to determine the actual size
    // this call should return ERROR_MORE_DATA for a REG_MULTI_SZ value

    // clear-up the old buffer
    if (pWinsCnf->pPersonaList != NULL)
    {
        WinsMscDealloc(pWinsCnf->pPersonaList);
    }
    pWinsCnf->NoOfPersona = 0;
    pWinsCnf->pPersonaList = NULL;

    // check if there is a valid value, with the expected type; return if not
    if (RetVal != ERROR_MORE_DATA || dwValType != REG_MULTI_SZ)
    {
        DBGLEAVE("GetOwnerList\n");
        return;
    }

    // allocate the needed buffer
    WinsMscAlloc(dwValDataLen, &pValData);

    // now query for the data value with a buffer large enough
    RetVal = RegQueryValueExA(
                sPartnersKey,
                pValName,
                NULL,                       // reserved; must be NULL
                &dwValType,
                (LPVOID)pValData,           // now this is the real address
                &dwValDataLen);

    // ERROR_SUCCESS is expected here
    if (RetVal == ERROR_SUCCESS)
    {
        LPBYTE pString = pValData;

        // count in nAddr the number of addresses in the string
        for( pWinsCnf->NoOfPersona=0; *pString; pWinsCnf->NoOfPersona++)
            pString+= strlen(pString)+1;

        // see if there are any addresses there
        if (pWinsCnf->NoOfPersona > 0)
        {
            COMM_IP_ADD_T IpAdd;

            // allocate an array of nAddr COMM_ADD_T structures
            WinsMscAlloc(
                (pWinsCnf->NoOfPersona) * sizeof(COMM_ADD_T),
                &(pWinsCnf->pPersonaList));

            // loop through the string of addresses and convert 
            // them to COMM_IP_ADD_T structures
            for (pString = pValData, pWinsCnf->NoOfPersona = 0;
                *pString;
                pString += strlen(pString) + 1)
            {
                if ((IpAdd = inet_addr(pString)) != -1)
                {
                    // initialize a COMM_ADD_T structure only if the string token is
                    // indeed an IP address
                    (pWinsCnf->pPersonaList)[pWinsCnf->NoOfPersona].AddTyp_e = COMM_ADD_E_TCPUDPIP;
                    (pWinsCnf->pPersonaList)[pWinsCnf->NoOfPersona].AddLen = COMM_IP_ADD_SIZE;
                    (pWinsCnf->pPersonaList)[pWinsCnf->NoOfPersona].Add.IPAdd = ntohl(IpAdd);

                    DBGPRINT2(
                        DET,
                        "GetOwnerList: Address[%d] = %x\n",
                        pWinsCnf->NoOfPersona,
                        (pWinsCnf->pPersonaList)[pWinsCnf->NoOfPersona].Add.IPAdd);

                    pWinsCnf->NoOfPersona++;
                } //end 'if valid ip address'
            } //end 'for each token in the string'

            // if there are at least two addresses in the list,
            // sort them in ascending order
            if (pWinsCnf->NoOfPersona > 1)
            {
                qsort(pWinsCnf->pPersonaList,
                      (size_t)pWinsCnf->NoOfPersona,
                      sizeof(COMM_ADD_T),
                      ECommCompareAdd);
            }
            else
            {
                DBGPRINT0(DET, "GetOwnerList: No valid address found\n");
            } //end 'if there are more than two addresses picked up'

        } //end 'if there are any tokens at all'
    } //end 'if string could be read successfully from the registry'

    // the string buffer is no longer needed here
    WinsMscDealloc(pValData);

#ifdef WINSDBG
    {
        DWORD tstNAddrs;
        PCOMM_ADD_T tstPAddrs;
        DWORD i;

        DBGPRINT1(
            DET,
            "GetOwnerList: Persona %sGrata List:\n",
            pWinsCnf->fPersonaGrata ? "" : "Non-");

        tstNAddrs = pWinsCnf->NoOfPersona;
        tstPAddrs = pWinsCnf->pPersonaList;
        for (i = 0; i < tstNAddrs; i++)
        {
            DBGPRINT2(
                DET,
                "GetOwnerList:PersonaList[%d] = %08x\n",
                i,
                tstPAddrs[i].Add.IPAdd);
        }
    }
#endif

    DBGLEAVE("GetOwnerList\n");
    return;
}

LONG
ReadClusterIp(
    HKEY    KeyHandle,
    DWORD   *IpAddress
    )
{
    DWORD           Sz;
    LONG            RetVal;
    DWORD           ValTyp;
    TCHAR            DirPath[WINS_MAX_FILENAME_SZ];

    *IpAddress = 0;
    // Read the wins cluster name
    Sz = WINS_MAX_FILENAME_SZ * sizeof(TCHAR);
    RetVal = RegQueryValueEx(
                         KeyHandle,
                         WINSCNF_CLUSTER_RESOURCE_NM,
                         NULL,                //reserved; must be NULL
                         &ValTyp,
                         (LPBYTE)DirPath,
                         &Sz
                         );

    if ((RetVal == ERROR_SUCCESS) && (DirPath[0] != (TCHAR)EOS))
    {
         HCLUSTER     hCluster;
         HRESOURCE    hResource;
         CHAR         IpAddressStr[sizeof ("xxx.xxx.xxx.xxx")];
         WCHAR        IpAddressWStr[sizeof ("xxx.xxx.xxx.xxx")];
         DWORD        i;
         HMODULE      Dll1, Dll2;
         #define      FUNC_TBL_ENTRY( _Dll, _Name ) { _Dll, &(#_Name)[1], NULL }
         #define      CALL_FUNC( _Func)     (PVOID)( *FuncTbl[_Func].FuncHdl )
         enum {
             _OpenCluster,
             _OpenClusterResource,
             _CloseCluster,
             _CloseClusterResource,
             _ResUtilGetResourceDependentIPAddressProps
         };
         struct {
             HMODULE     *Dll;
             LPCSTR       FuncName;
             FARPROC      FuncHdl;
         } FuncTbl[] = {
             FUNC_TBL_ENTRY( &Dll1,_OpenCluster),
             FUNC_TBL_ENTRY( &Dll1,_OpenClusterResource),
             FUNC_TBL_ENTRY( &Dll1,_CloseCluster),
             FUNC_TBL_ENTRY( &Dll1,_CloseClusterResource),
             FUNC_TBL_ENTRY( &Dll2,_ResUtilGetResourceDependentIPAddressProps)
         };


         DBGPRINT1(DET, "WinsCnfReadWinsInfo: ClusterResourceName is (%ws)\n", DirPath);
         hCluster = NULL;
         hResource = NULL;
         Dll1 = Dll2 = NULL;
        do {
             Dll1 = LoadLibrary(TEXT("clusapi.dll"));
             if (!Dll1) {
                 RetVal = GetLastError();
                 break;
             }
             Dll2 = LoadLibrary(TEXT("resutils.dll"));
             if (!Dll2) {

                 RetVal = GetLastError();
                 break;
             }
             for (i=0; i<(sizeof(FuncTbl)/sizeof(FuncTbl[0])); i++) {
                 FuncTbl[i].FuncHdl = GetProcAddress(*FuncTbl[i].Dll, FuncTbl[i].FuncName);
                 if (!FuncTbl[i].FuncHdl) {
                     RetVal = GetLastError();
                     break;
                 }
             }
             if (i<(sizeof(FuncTbl)/sizeof(FuncTbl[0]))) {
                 break;
             }
             hCluster = CALL_FUNC(_OpenCluster)( NULL );
             if (!hCluster) {
                 RetVal = GetLastError();
                 break;
             }
             hResource = CALL_FUNC(_OpenClusterResource)(hCluster, DirPath);
             if (!hResource) {
                 RetVal = GetLastError();
                 break;
             }
             Sz = sizeof (IpAddressWStr);
             RetVal = PtrToLong(CALL_FUNC(_ResUtilGetResourceDependentIPAddressProps)(
                         hResource,
                         IpAddressWStr,
                         &Sz,
                         NULL,
                         NULL,
                         NULL,
                         NULL
                         ));
             if (ERROR_SUCCESS != RetVal) {
                 break;
             }
             WinsMscConvertUnicodeStringToAscii((LPBYTE)IpAddressWStr, IpAddressStr, sizeof(IpAddressStr));
             *IpAddress = ntohl(inet_addr(IpAddressStr));
             DBGPRINT1(DET, "ReadClusterIp: Cluster IpAddress is (%lx)\n", *IpAddress);
        } while ( FALSE );
        if( hResource ) CALL_FUNC(_CloseClusterResource)(hResource);
        if( hCluster ) CALL_FUNC(_CloseCluster) (hCluster);
        if ( Dll1 ) FreeLibrary(Dll1);
        if ( Dll2 ) FreeLibrary(Dll2);
    }

    return RetVal;
}

extern DOM_CACHE_T sDomCache;

VOID
WinsCnfReadWinsInfo(
        PWINSCNF_CNF_T pWinsCnf
        )

/*++

Routine Description:
        This function reads information (excluding subkeys) about
        the local WINS

Arguments:
        None

Externals Used:
        sConfigRoot,

Return Value:
        None

Error Handling:

Called by:
        WinsCnfInitConfig

Side Effects:

Comments:
        None
--*/

{

        DWORD           Sz;
        LONG            RetVal;
        DWORD           ValTyp;
        VERS_NO_T       MaxVersNo;
        WINSEVT_STRS_T  EvtStr;
        TCHAR            DirPath[WINS_MAX_FILENAME_SZ];
        TCHAR            Path2[WINS_MAX_FILENAME_SZ];
        LPTSTR          pHoldFileName;
        DWORD           fUse351Db;
        DWORD           fUse4Db;
        EvtStr.NoOfStrs = 1;

try {

#if defined(DBGSVC) && !defined(WINS_INTERACTIVE)
        //
        // Read the value of WinsDbg. Though this value is
        // being used concurrently by multiple threads (at reinit time), we
        // don't enter any critical section here.  This value
        // is used only for debugging
        //
        WinsCnfReadWinsDbgFlagValue();
#endif

        Sz = sizeof(pWinsCnf->LogDetailedEvts);
        (VOID)RegQueryValueEx(
                             sParametersKey,
                             WINSCNF_LOG_DETAILED_EVTS_NM,
                             NULL,        //reserved; must be NULL
                             &ValTyp,
                             (LPBYTE)&pWinsCnf->LogDetailedEvts,
                             &Sz
                                );

        // Read in the 1B filter. If the "Filter1BRequests" is there and it is a REG_MULTI_SZ
        // then the filter is created for each of the names specified there. When R_WinsGetBrowserNames
        // is called, the database is filtered only for the name present in the filter.
        RetVal = RegQueryValueExW(
                    sParametersKey,
                    WINSCNF_FILTER1BREQUESTS_NM,
                    NULL,
                    &ValTyp,
                    NULL,
                    &Sz);

        if (RetVal == ERROR_SUCCESS && ValTyp == REG_MULTI_SZ)
        {
            LPWSTR str1BFilters = NULL;
            LPWSTR p1BFilter;

            WinsMscAlloc(Sz, &str1BFilters);

            if (RegQueryValueExW(
                    sParametersKey,
                    WINSCNF_FILTER1BREQUESTS_NM,
                    NULL,
                    &ValTyp,
                    (LPBYTE)str1BFilters,
                    &Sz) == ERROR_SUCCESS)
            {
                EnterCriticalSection(&g_cs1BFilter);

                try
                {
                    g_p1BFilter = InitNmFilter(g_p1BFilter);

                    p1BFilter = str1BFilters;
                    do
                    {
                        DWORD nLenFilter = wcslen(p1BFilter);
                        CHAR  strOemName[17];
                        OEM_STRING      oemString;
                        UNICODE_STRING  unicodeString;

                        if (nLenFilter == 0)
                            break;

                        memset(strOemName, ' ', 16);
                        strOemName[16]=0;
                        if (nLenFilter > 15)
                            p1BFilter[16]=L'\0';

                        RtlInitUnicodeString(&unicodeString, p1BFilter);
                        RtlInitString(&oemString, strOemName);
                        RtlUpcaseUnicodeStringToOemString(&oemString, &unicodeString, FALSE);

                        strOemName[strlen(strOemName)] = ' ';
                        strOemName[15] = strOemName[0];
                        strOemName[0]  = 0x1B;
                        InsertNmInFilter(g_p1BFilter, strOemName, 16);
                        p1BFilter += nLenFilter+1;
                    }
                    while(TRUE);
                }
                finally
                {
                    LeaveCriticalSection(&g_cs1BFilter);
                }
            }

            WinsMscDealloc(str1BFilters);
        }
        else
        {
            // if the reg key is not there, reset the filter - all names will be returned.
            EnterCriticalSection(&g_cs1BFilter);
            try
            {
                g_p1BFilter = DestroyNmFilter(g_p1BFilter);
            }
            finally
            {
                g_p1BFilter = NULL;
                LeaveCriticalSection(&g_cs1BFilter);
            }
        }

        sDomCache.bRefresh = TRUE;

        //
        // Read in the fAdd1Bto1CQueries parameter. Default is TRUE
        // meaning: when processing name queries for 1C names, prepend the
        // response with the 1B name (browser name).
        //
        pWinsCnf->fAdd1Bto1CQueries = TRUE;
        Sz = sizeof(pWinsCnf->fAdd1Bto1CQueries);
        (VOID)RegQueryValueEx(
                            sParametersKey,
                            WINSCNF_ADD1BTO1CQUERIES_NM,
                            NULL,
                            &ValTyp,
                            (LPBYTE)&pWinsCnf->fAdd1Bto1CQueries,
                            &Sz
                            );
        //
        // Read in the cap value on the number of worker threads.
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_MAX_NO_WRK_THDS_NM,
                        ValTyp,
                        pWinsCnf->MaxNoOfWrkThds,
                        0,
                        0   //WINSTHD_DEF_NO_NBT_THDS
                      );

        //
        // Check if the user needs to override our checks.
        //
        sfNoLimitChk = pWinsCnf->MaxNoOfWrkThds & NO_LIMIT_CHK_FLAG;
        if (sfNoLimitChk)
        {
            WINSEVT_LOG_M(pWinsCnf->LogDetailedEvts, WINS_EVT_INTERNAL_FEATURE);
            pWinsCnf->MaxNoOfWrkThds &= ~NO_LIMIT_CHK_FLAG;
        }

        if (pWinsCnf->MaxNoOfWrkThds > WINSTHD_MAX_NO_NBT_THDS)
        {
             pWinsCnf->MaxNoOfWrkThds = WINSTHD_MAX_NO_NBT_THDS;
        }
        if (pWinsCnf->MaxNoOfWrkThds < WINSTHD_MIN_NO_NBT_THDS)
        {
             pWinsCnf->MaxNoOfWrkThds = WINSTHD_MIN_NO_NBT_THDS;
        }

#if 0
        if (WinsCnf.State_e == WINSCNF_E_INITING)
        {
                ReadSpecGrpMasks(pWinsCnf);
        }
#endif

#if DYNLOADJET
        //
        // Read in the cap value on the number of worker threads.
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_USE_351DB_NM,
                        ValTyp,
                        fUse351Db,
                        0,
                        0   //Use 500 db
                      );
        //
        // If set to a non-zero value, we need to load jet.dll
        //
        if (fUse351Db)
        {
            DynLoadJetVersion = DYN_LOAD_JET_200;
        } else {
            QUERY_VALUE_M(
                            sParametersKey,
                            WINSCNF_USE_4DB_NM,
                            ValTyp,
                            fUse4Db,
                            0,
                            0   //Use 500 db
                          );
            //
            // If set to a non-zero value, we need to load jet.dll
            //
            if (fUse4Db)
            {
                DynLoadJetVersion = DYN_LOAD_JET_500;
            }
        }

#endif
        //
        // Read in the refresh Interval
        //
        Sz = sizeof(pWinsCnf->RefreshInterval);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_REFRESH_INTVL_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->RefreshInterval,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_REFRESH_INTERVAL_VAL
                               );
                pWinsCnf->RefreshInterval = WINSCNF_DEF_REFRESH_INTERVAL;
        }
        else
        {
                if (!sfNoLimitChk)
                {
                    if (pWinsCnf->RefreshInterval  <
                                   WINSCNF_MIN_REFRESH_INTERVAL)
                    {
                       pWinsCnf->RefreshInterval = WINSCNF_MIN_REFRESH_INTERVAL;
                       WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_REFRESH_INTVL_NM,
                             pWinsCnf->RefreshInterval);
                    }
                }
                else
                {
                      if (pWinsCnf->RefreshInterval < 60)
                      {
                          pWinsCnf->RefreshInterval = 60;
                      }
                }
        }

        //
        // Read in the Initial Challenge Retry Interval
        //
        Sz = sizeof(pWinsCnf->RetryInterval);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_INIT_CHL_RETRY_INTVL_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->RetryInterval,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_INIT_CHL_RETRY_INTVL_VAL
                               );
                pWinsCnf->RetryInterval = WINSCNF_DEF_INIT_CHL_RETRY_INTVL;
        }
        else
        {
                if (!sfNoLimitChk)
                {
                    if (pWinsCnf->RetryInterval  <
                                   WINSCNF_MIN_INIT_CHL_RETRY_INTVL)
                    {
                       pWinsCnf->RetryInterval = WINSCNF_MIN_INIT_CHL_RETRY_INTVL;
                       WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_INIT_CHL_RETRY_INTVL_NM,
                             pWinsCnf->RetryInterval);
                    }
                }
                else
                {
                      if (pWinsCnf->RetryInterval < WINSCNF_MIN_INIT_CHL_RETRY_INTVL)
                      {
                          pWinsCnf->RetryInterval = WINSCNF_MIN_INIT_CHL_RETRY_INTVL;
                      }
                }
        }

        //
        // Read in the Initial Challenge Max. No. of Retries
        //
        Sz = sizeof(pWinsCnf->MaxNoOfRetries);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_CHL_MAX_RETRIES_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->MaxNoOfRetries,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_CHL_MAX_RETRIES_VAL
                               );
                pWinsCnf->MaxNoOfRetries = WINSCNF_DEF_CHL_MAX_RETRIES;
        }
        else
        {
                if (!sfNoLimitChk)
                {
                    if (pWinsCnf->MaxNoOfRetries  <
                                   WINSCNF_MIN_CHL_MAX_RETRIES)
                    {
                       pWinsCnf->MaxNoOfRetries = WINSCNF_MIN_CHL_MAX_RETRIES;
                       WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_CHL_MAX_RETRIES_NM,
                             pWinsCnf->MaxNoOfRetries);
                    }
                }
                else
                {
                      if (pWinsCnf->MaxNoOfRetries < WINSCNF_MIN_CHL_MAX_RETRIES)
                      {
                          pWinsCnf->MaxNoOfRetries = WINSCNF_MIN_CHL_MAX_RETRIES;
                      }
                }
        }

        //
        // Read in the tombstone Interval
        //
        Sz = sizeof(pWinsCnf->TombstoneInterval);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_TOMBSTONE_INTVL_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->TombstoneInterval,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_TOMBSTONE_INTERVAL_VAL
                               );

                pWinsCnf->TombstoneInterval =
                        WINSCNF_MAKE_TOMB_INTVL_0_M(pWinsCnf->RefreshInterval);
        }
        else
        {

                if ( !sfNoLimitChk)
                {
                      if (pWinsCnf->TombstoneInterval <
                           WINSCNF_MAKE_TOMB_INTVL_0_M(pWinsCnf->RefreshInterval) )
                      {
                        pWinsCnf->TombstoneInterval =
                           WINSCNF_MAKE_TOMB_INTVL_0_M(pWinsCnf->RefreshInterval);

                        WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_TOMBSTONE_INTVL_NM,
                             pWinsCnf->TombstoneInterval);
                     }
                }
                else
                {
                     if (pWinsCnf->TombstoneInterval < 60)
                     {
                        pWinsCnf->TombstoneInterval = 60;
                     }
                }

        }

        //
        // Read in the tombstone timeout
        //
        Sz = sizeof(pWinsCnf->TombstoneTimeout);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_TOMBSTONE_TMOUT_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->TombstoneTimeout,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_TOMBSTONE_TIMEOUT_VAL
                               );
                pWinsCnf->TombstoneTimeout = pWinsCnf->RefreshInterval;
        }
        else
        {
                if (!sfNoLimitChk)
                {
                  if (pWinsCnf->TombstoneTimeout < pWinsCnf->RefreshInterval)
                  {
                    pWinsCnf->TombstoneTimeout = pWinsCnf->RefreshInterval;
                    WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_TOMBSTONE_TMOUT_NM,
                             pWinsCnf->TombstoneTimeout);
                  }
               }
               else
               {
                   if (pWinsCnf->TombstoneTimeout < 60)
                   {
                      pWinsCnf->TombstoneTimeout = 60;
                   }
               }
        }

        //
        // Read in the Verify Interval
        //
        Sz = sizeof(pWinsCnf->VerifyInterval);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_VERIFY_INTVL_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->VerifyInterval,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_VERIFY_INTERVAL_VAL
                               );

                pWinsCnf->VerifyInterval =
                   WINSCNF_MAKE_VERIFY_INTVL_M(pWinsCnf->TombstoneInterval);
        }
        else
        {

                if ( !sfNoLimitChk)
                {
                   if (pWinsCnf->VerifyInterval <
                       WINSCNF_MAKE_VERIFY_INTVL_M(pWinsCnf->TombstoneInterval))
                   {
                        pWinsCnf->VerifyInterval =
                           WINSCNF_MAKE_VERIFY_INTVL_M(pWinsCnf->TombstoneInterval);
                        WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_VERIFY_INTVL_NM,
                             pWinsCnf->VerifyInterval);

                  }
               }
               else
               {
                   if (pWinsCnf->VerifyInterval < 60)
                   {
                         pWinsCnf->VerifyInterval = 60;
                   }
               }

        }

        ReadCCInfo(pWinsCnf);

        //
        // Check if the admin. wants us to do pull/push replications with
        // pnrs found by self.
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_BURST_HANDLING_NM,
                        ValTyp,
                        pWinsCnf->fDoSpoofing,
                        0,
                        TRUE
                      );
       if (pWinsCnf->fDoSpoofing)
       {
          pWinsCnf->fDoSpoofing = TRUE;  //for robustness
       }

#if MCAST > 0
        //
        // Check if the admin. wants us to do pull/push replications with
        // pnrs found by self.
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_USE_SELF_FND_PNRS_NM,
                        ValTyp,
                        pWinsCnf->fUseSelfFndPnrs,
                        0,
                        FALSE
                      );

        if (pWinsCnf->fUseSelfFndPnrs)
        {
                 if (WinsCnf.State_e == WINSCNF_E_INITING)
                 {
                        Sz = sizeof(pWinsCnf->McastTtl);
                        RetVal = RegQueryValueEx(
                                            sParametersKey,
                                            WINSCNF_MCAST_TTL_NM,
                                            NULL,
                                            &ValTyp,
                                            (LPBYTE)&pWinsCnf->McastTtl,
                                             &Sz
                                         );

                        if (RetVal != ERROR_SUCCESS)
                        {
                            pWinsCnf->McastTtl = WINSCNF_DEF_MCAST_TTL;
                        }
                        else
                        {
                           if (
                             (pWinsCnf->McastTtl < WINSCNF_MIN_MCAST_TTL)
                                          ||
                             (pWinsCnf->McastTtl > WINSCNF_MAX_MCAST_TTL)
                               )
                           {
                             pWinsCnf->McastTtl = WINSCNF_DEF_MCAST_TTL;
                           }
                        }
                 }

                 Sz = sizeof(pWinsCnf->McastIntvl);
                 RetVal = RegQueryValueEx(
                                            sParametersKey,
                                            WINSCNF_MCAST_INTVL_NM,
                                            NULL,
                                            &ValTyp,
                                            (LPBYTE)&pWinsCnf->McastIntvl,
                                             &Sz
                                        );

                 if (RetVal != ERROR_SUCCESS)
                 {
                        pWinsCnf->McastIntvl = WINSCNF_DEF_MCAST_INTVL;
                 }
                 else
                 {
                        if ( pWinsCnf->McastIntvl < WINSCNF_MIN_MCAST_INTVL )
                        {
                               pWinsCnf->McastIntvl = WINSCNF_MIN_MCAST_INTVL;
                        }
                 }
         }
#endif
        //
        // Check if replication is to be done only with configured partners.
        // If set to TRUE, it means that an administrator will not be allowed
        // to trigger replication to/from a WINS that this WINS does not know
        // about.  Default value is FALSE
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_RPL_ONLY_W_CNF_PNRS_NM,
                        ValTyp,
                        pWinsCnf->fRplOnlyWCnfPnrs,
                        0,
                        TRUE
                      );
        //
        // Robust programming (in case the registry has a value other than 1
        // and later on we compare the value with TRUE)
        //
        if (pWinsCnf->fRplOnlyWCnfPnrs != FALSE)
        {
                pWinsCnf->fRplOnlyWCnfPnrs = TRUE;
        }

        if (WinsCnf.State_e == WINSCNF_E_INITING)
        {

           (VOID)WinsMscAlloc(WINS_MAX_FILENAME_SZ, &(pWinsCnf->pWinsDb));
           //
           // Read in the name of the database file
           //
FUTURES("when jet supports UNICODE in its api, change this")
           Sz = WINS_MAX_FILENAME_SZ * sizeof(TCHAR);
           RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_DB_FILE_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)DirPath,
                                &Sz
                                );

           if ((RetVal != ERROR_SUCCESS) || (DirPath[0] == (TCHAR)EOS))
           {
               WinsMscDealloc(pWinsCnf->pWinsDb);
               pWinsCnf->pWinsDb = WINSCNF_DB_NAME_ASCII;
           }
           else
           {
                 if(!WinsMscGetName(ValTyp, DirPath, Path2, WINS_MAX_FILENAME_SZ, &pHoldFileName))
                 {
                      WinsMscDealloc(pWinsCnf->pWinsDb);
                      pWinsCnf->pWinsDb = WINSCNF_DB_NAME_ASCII;
                 }
                 else
                 {
                      WinsMscConvertUnicodeStringToAscii((LPBYTE)pHoldFileName, pWinsCnf->pWinsDb, WINS_MAX_FILENAME_SZ);
                      DBGPRINT1(DET, "WinsCnfReadWinsInfo: Db file path is (%s)\n", pWinsCnf->pWinsDb);
                 }
           }

           RetVal = ReadClusterIp(
                        sParametersKey,
                        &WinsClusterIpAddress
                        );
           if (ERROR_SUCCESS == RetVal) {
               NmsLocalAdd.Add.IPAdd = WinsClusterIpAddress;
           } else {
               DBGPRINT1(DET, "ReadClusterIp: Returned (%ld)\n", RetVal);
           }
        }

        //
        // Read in the PriorityClassHigh value.  Default is normal
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_PRIORITY_CLASS_HIGH_NM,
                        ValTyp,
                        pWinsCnf->WinsPriorityClass,
                        0,
                        WINSINTF_E_NORMAL
                      );

        if (pWinsCnf->WinsPriorityClass != WINSINTF_E_NORMAL)
        {
             if (WinsCnf.WinsPriorityClass != WINSINTF_E_HIGH)
             {
                  WinsSetPriorityClass(WINSINTF_E_HIGH);
             }
        }
        else
        {
             if (WinsCnf.WinsPriorityClass != WINSINTF_E_NORMAL)
             {
                  WinsSetPriorityClass(WINSINTF_E_NORMAL);
             }
        }

        if (WinsCnf.State_e == WINSCNF_E_INITING)
        {
          //
          // Read in the InitTimeState value.  Default is FALSE
          //
          QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_INIT_TIME_PAUSE_NM,
                        ValTyp,
                        fWinsCnfInitStatePaused,
                        0,
                        FALSE
                      );

        //
        // Read in the name of the recovery file
        //
        (VOID)WinsMscAlloc(WINS_MAX_FILENAME_SZ, &(pWinsCnf->pLogFilePath));
        Sz = WINS_MAX_FILENAME_SZ * sizeof(TCHAR);
        RetVal = RegQueryValueEx(
                        sParametersKey,
                        WINSCNF_LOG_FILE_PATH_NM,
                        NULL,          //reserved; must be NULL
                        &ValTyp,
                        (LPBYTE)DirPath,
                        &Sz
                                );

        if ((RetVal != ERROR_SUCCESS) || (DirPath[0] == (TCHAR)EOS))
        {
                DBGPRINT1(ERR, "WinsCnfReadInfo: RetVal=(%x)\n", RetVal);
                WinsMscDealloc(pWinsCnf->pLogFilePath);
                pWinsCnf->pLogFilePath = DEFAULT_LOG_PATH;
        }
        else
        {
                 if(!WinsMscGetName(ValTyp, DirPath, Path2, WINS_MAX_FILENAME_SZ, &pHoldFileName))
                 {
                DBGPRINT0(ERR, "WinsCnfReadInfo:WinsMscGetName returned FALSE\n");
                    WinsMscDealloc(pWinsCnf->pLogFilePath);
                    pWinsCnf->pLogFilePath = DEFAULT_LOG_PATH;
                 }
                 else
                 {
                DBGPRINT1(DET, "WinsCnfReadInfo:pHoldFileName=%s\n", pHoldFileName);
                      WinsMscConvertUnicodeStringToAscii((LPBYTE)pHoldFileName, pWinsCnf->pLogFilePath, WINS_MAX_FILENAME_SZ);

                 }
        }

        //
        // Check if user wants logging to be turned on.
        // In case of Q servers, the user would not wish the logging to be
        // turned on
        //
        Sz = sizeof(pWinsCnf->fLoggingOn);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_LOG_FLAG_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->fLoggingOn,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                //
                // default is to turn on logging
                //
                pWinsCnf->fLoggingOn = TRUE;
        }
        else
        {
                //
                // If user has specified logging, get the path to the log
                // file if specified by user
                //
                if (pWinsCnf->fLoggingOn)
                {
                        pWinsCnf->fLoggingOn = TRUE;
                }
        }

       }
        //
        // Check to see if STATIC initialization of the WINS database needs
        // to be done
        //
        Sz = sizeof(pWinsCnf->fStaticInit);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_STATIC_INIT_FLAG_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->fStaticInit,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                pWinsCnf->fStaticInit = FALSE;
        }
        else
        {
                //
                // Safe programming (just in case a maintainer of this code
                // assumes a BOOL field to have just TRUE and FALSE values
                //
                pWinsCnf->fStaticInit = pWinsCnf->fStaticInit > 0 ? TRUE : FALSE;
        }

        //
        // If Static Initialization needs to be done, read in the name of
        // the file that contains the data.
        //
        if(pWinsCnf->fStaticInit)
        {
                WinsCnfGetNamesOfDataFiles(pWinsCnf);
        }


        //
        // Assign MaxVersNo with the default value
        //
        WINS_ASSIGN_INT_TO_VERS_NO_M(MaxVersNo, 0);

        //
        // If the WINS server is just coming up (i.e. it is not a reinit),
        // then read in the starting value of the version number counter
        // if present in the registry
        //
        Sz = sizeof(DWORD);
        (VOID)RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_INIT_VERSNO_VAL_LW_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&MaxVersNo.LowPart,
                                &Sz
                                );

        (VOID)RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_INIT_VERSNO_VAL_HW_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&MaxVersNo.HighPart,
                                &Sz
                                );

        //
        // if we read in a value for the version counter
        //
        if (LiGtrZero(MaxVersNo) && (MaxVersNo.HighPart == 0) &&
                             (MaxVersNo.LowPart < MAX_START_VERS_NO))
        {
          //
          // Use WinsCnf, not pWinsCnf since at initialization time, we always
          // read into WinsCnf. At reinit, the State_e field in the WinsCnf
          // structure allocated may have garbage (actually will be 0 since we
          // initialize allocated memory to zero -- I might change in the
          // future to improve performance)
          //
          if (WinsCnf.State_e == WINSCNF_E_INITING)
          {
            //
            // Min. Vers. to start scavenging from.
            //
            //  NOTE: if we find local records or the special record then
            //  NmsScvMinScvVersNo will get changed (check out GetMaxVersNos
            //  in nmsdb.c
            //
            NmsNmhMyMaxVersNo  = MaxVersNo;
            NmsScvMinScvVersNo = NmsNmhMyMaxVersNo;
            NmsVersNoToStartFromNextTime.QuadPart =
                        LiAdd(NmsNmhMyMaxVersNo, NmsRangeSize);

            NmsHighWaterMarkVersNo.QuadPart =
                        LiAdd(NmsNmhMyMaxVersNo, NmsHalfRangeSize);
          }
          else  // this must be a reconfiguration
          {
                EnterCriticalSection(&NmsNmhNamRegCrtSec);

                //
                // change the value of the version counter if
                // the new value is more than it.
                //
                if (LiGtr(MaxVersNo, NmsNmhMyMaxVersNo))
                {
                        NmsNmhMyMaxVersNo = MaxVersNo;
                        WINSEVT_LOG_INFO_M(MaxVersNo.LowPart,
                                WINS_EVT_VERS_COUNTER_CHANGED);
                }
                NmsVersNoToStartFromNextTime.QuadPart =
                        LiAdd(NmsNmhMyMaxVersNo, NmsRangeSize);
                NmsHighWaterMarkVersNo.QuadPart =
                        LiAdd(NmsNmhMyMaxVersNo, NmsHalfRangeSize);
                LeaveCriticalSection(&NmsNmhNamRegCrtSec);
          }
        }

        if (WinsCnf.State_e == WINSCNF_E_INITING)
        {
           //
           // Check if a port has been assigned by the user.
           //
           QUERY_VALUE_M(
                                sParametersKey,
                                WINSCNF_WINS_PORT_NO_NM,
                                ValTyp,
                                CommWinsTcpPortNo,
                                0,
                                COMM_DEFAULT_IP_PORT
                             );
           DBGPRINT1(DET, "WinsCnfReadWinsInfo: Port No is (%d)\n", CommWinsTcpPortNo);

          //
          // Check if WINS should continue replication in case of an
          // error in replication.
          //
          QUERY_VALUE_M(
                                sParametersKey,
                                WINSCNF_NO_RPL_ON_ERR_NM,
                                ValTyp,
                                WinsCnf.fNoRplOnErr,
                                0,
                                TRUE
                             );


          //
          // Assign MaxVersNo with the default value
          //
          WINS_ASSIGN_INT_TO_VERS_NO_M(MaxVersNo, 0);

          //
          // Read in the value specified in the registry for the version
          // number that we should use when starting.
          //
          Sz = sizeof(DWORD);
          (VOID)RegQueryValueEx(
                                sConfigRoot,
                                WINSCNF_INT_VERSNO_NEXTTIME_LW_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&MaxVersNo.LowPart,
                                &Sz
                                );

          (VOID)RegQueryValueEx(
                                sConfigRoot,
                                WINSCNF_INT_VERSNO_NEXTTIME_HW_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&MaxVersNo.HighPart,
                                &Sz
                                );

          //
          // if we read in a value for the version counter and it is greater
          // than the high-water mark currently there
          //
          if (LiGtr(MaxVersNo, NmsHighWaterMarkVersNo))
          {
               fWinsCnfReadNextTimeVersNo = TRUE;

               //
               // Use WinsCnf, not pWinsCnf since at initialization time,
               // we always read into WinsCnf. At reinit, the State_e field
               // in the WinsCnf structure allocated may have garbage
               // (actually will be 0 since we initialize allocated memory
               // to zero -- I might change in the future to improve
               // performance)
               //

               //
               // Min. Vers. to start scavenging from.
               //
               //  NOTE: if we find local records or the special record then
               //  NmsScvMinScvVersNo will get changed (check out GetMaxVersNos
               //  in nmsdb.c
               //
               if (LiLtr(NmsNmhMyMaxVersNo, MaxVersNo))
               {
                   NmsNmhMyMaxVersNo  = MaxVersNo;
                   NmsScvMinScvVersNo = NmsNmhMyMaxVersNo;
               }
               NmsVersNoToStartFromNextTime.QuadPart =
                       LiAdd(NmsNmhMyMaxVersNo,  NmsRangeSize);
               NmsHighWaterMarkVersNo.QuadPart =
                        LiAdd(NmsNmhMyMaxVersNo, NmsHalfRangeSize);
         }
       }  //end of if state is INITING

        //
        // Check to see if a backup directory has been specified for the WINS
        // database
        //
        //
        // Read in the name of the recovery file
        //
        Sz = WINS_MAX_FILENAME_SZ * sizeof(TCHAR);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_BACKUP_DIR_PATH_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)DirPath,
                                &Sz
                                );

        if ((RetVal != ERROR_SUCCESS) || (DirPath[0] == (TCHAR)EOS))
        {
                pWinsCnf->pBackupDirPath = NULL;
        }
        else
        {
                 if(!WinsMscGetName(ValTyp, DirPath, Path2, WINS_MAX_FILENAME_SZ, &pHoldFileName))
                 {
                    pWinsCnf->pBackupDirPath = NULL;
                 }
                 else
                 {
                      WinsMscAlloc(Sz + sizeof(WINS_BACKUP_DIR_ASCII), &pWinsCnf->pBackupDirPath);
FUTURES("When Jet starts taking UNICODE input, get rid of this")
                      WinsMscConvertUnicodeStringToAscii((LPBYTE)pHoldFileName, pWinsCnf->pBackupDirPath, WINS_MAX_FILENAME_SZ);
                      strcat(pWinsCnf->pBackupDirPath, WINS_BACKUP_DIR_ASCII);

                      //
                      // No need to look at the return code.
                      //
                      CreateDirectoryA(pWinsCnf->pBackupDirPath, NULL);

                 }

        }

        //
        // Check to see if the admin. has told WINS to do a backup on
        // termination
        //
        Sz = sizeof(pWinsCnf->fDoBackupOnTerm);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_DO_BACKUP_ON_TERM_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->fDoBackupOnTerm,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                pWinsCnf->fDoBackupOnTerm = FALSE;
        }

        //
        // Check to see if static records have to be treated as p-static
        //
        Sz = sizeof(pWinsCnf->fPStatic);
        RetVal = RegQueryValueEx(
                                sParametersKey,
                                WINSCNF_MIGRATION_ON_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->fPStatic,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                pWinsCnf->fPStatic = FALSE;
        }

        //
        // Read max wins registration que len.
        //
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_BURST_QUE_SIZE_NM,
                        ValTyp,
                        QueOtherNbtWrkQueMaxLen,
                        0,
                        WINS_QUEUE_HWM
                      );

        if (QueOtherNbtWrkQueMaxLen < WINS_QUEUE_HWM_MIN) {
            QueOtherNbtWrkQueMaxLen = WINS_QUEUE_HWM_MIN;
        } else if (QueOtherNbtWrkQueMaxLen > WINS_QUEUE_HWM_MAX) {
            QueOtherNbtWrkQueMaxLen = WINS_QUEUE_HWM_MAX;
        }

        // Read whether or not we randomize 1c list retrieval.
        QUERY_VALUE_M(
                        sParametersKey,
                        WINSCNF_RANDOMIZE_1C_LIST_NM,
                        ValTyp,
                        WinsCnf.fRandomize1CList,
                        0,
                        FALSE
                      );

} // end of try ..
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsCnfReadWinsInfo");
        WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_SFT_ERR);
        }
        return;
}

VOID
ReadCCInfo(
 PWINSCNF_CNF_T  pWinsCnf
)

/*++

Routine Description:
         Function to read in CC info

Arguments:


Externals Used:
	None

	
Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
	None
--*/

{

        SYSTEMTIME CurrTime;
        DWORD           Sz;
        LONG            RetVal;
        DWORD           ValTyp;
        VERS_NO_T       MaxVersNo;
        WINSEVT_STRS_T  EvtStr;

        DBGENTER("ReadCCInfo\n");

        //
        // Open the Consistency Chk Key
        //

        RetVal =   RegOpenKeyEx(
                        sConfigRoot,           //predefined key value
                        _WINS_CFG_CC_KEY,
                        0,                     //must be zero (reserved)
                        KEY_READ | KEY_WRITE,  //we desire read/write access
                                               // to the key
                        &sCCKey                //handle to key
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_OPEN_CC_KEY
                                   );
                pWinsCnf->CC.TimeInt = MAXULONG;
                return;
        }

        Sz = sizeof(pWinsCnf->CC.TimeInt);
        RetVal = RegQueryValueEx(
                                sCCKey,
                                WINSCNF_CC_INTVL_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->CC.TimeInt,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_CC_INTERVAL_VAL
                               );

                pWinsCnf->CC.TimeInt = WINSCNF_CC_DEF_INTERVAL;
        }
        else
        {

                if ( !sfNoLimitChk)
                {
                   if (pWinsCnf->CC.TimeInt <  WINSCNF_CC_MIN_INTERVAL)
                   {
                        pWinsCnf->CC.TimeInt = WINSCNF_CC_MIN_INTERVAL;
                        WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL,
                             NULL, __LINE__, "ud",
                             WINSCNF_CC_INTVL_NM,
                             pWinsCnf->CC.TimeInt);

                  }
               }
               else
               {
                   if (pWinsCnf->CC.TimeInt < 60)
                   {
                         pWinsCnf->CC.TimeInt = 60;
                   }
               }

        }

        Sz = sizeof(pWinsCnf->CC.MaxRecsAAT);
        RetVal = RegQueryValueEx(
                                sCCKey,
                                WINSCNF_CC_MAX_RECS_AAT_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->CC.MaxRecsAAT,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_CC_MAX_RECS_AAT_VAL
                               );

                pWinsCnf->CC.MaxRecsAAT = WINSCNF_CC_DEF_RECS_AAT;
        }
        else
        {
                if (pWinsCnf->CC.MaxRecsAAT <  WINSCNF_CC_MIN_RECS_AAT)
                {
                   if ( !sfNoLimitChk)
                   {
                        WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_MAX_RECS_AAT,
                             NULL, __LINE__, "udd",
                             WINSCNF_CC_MAX_RECS_AAT_NM,
                             WINSCNF_CC_MIN_RECS_AAT,
                             pWinsCnf->CC.MaxRecsAAT);
                        pWinsCnf->CC.MaxRecsAAT = WINSCNF_CC_MIN_RECS_AAT;
                   }
                   else
                   {
                      if (pWinsCnf->CC.MaxRecsAAT < 2)
                      {
                        pWinsCnf->CC.MaxRecsAAT = 2;
                      }

                   }

               }
        }

        Sz = sizeof(pWinsCnf->CC.fUseRplPnrs);
        RetVal = RegQueryValueEx(
                                sCCKey,
                                WINSCNF_CC_USE_RPL_PNRS_NM,
                                NULL,                //reserved; must be NULL
                                &ValTyp,
                                (LPBYTE)&pWinsCnf->CC.fUseRplPnrs,
                                &Sz
                                );

        if (RetVal != ERROR_SUCCESS)
        {
                WINSEVT_LOG_INFO_D_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_GET_CC_USE_RPL_PNRS_VAL
                               );

                pWinsCnf->CC.fUseRplPnrs = WINSCNF_CC_DEF_USE_RPL_PNRS;
        }

        GetLocalTime(&CurrTime);
        GetSpTimeData(sCCKey, &CurrTime, &pWinsCnf->CC.fSpTime, &pWinsCnf->CC.SpTimeInt);
        REG_M(
                     RegCloseKey(sCCKey),
                     WINS_EVT_CANT_CLOSE_KEY,
                     WINS_EXC_CANT_CLOSE_KEY
                );
        DBGLEAVE("ReadCCInfo\n");
        return;
}
#if USENETBT > 0
//------------------------------------------------------------------------
STATUS
WinsCnfReadNbtDeviceName(
        VOID
    )

/*++

Routine Description:

    This procedure reads the registry to get the name of NBT to bind to.
    That name is stored in the Linkage section under the Netbt key.

Arguments:


Return Value:

    0 if successful, -1 otherwise.

--*/

{
    PTCHAR  SubKeyLinkage=NETBT_LINKAGE_KEY;
    HKEY    Key;
    PTCHAR  pLinkage=TEXT("Export");
    LONG    Type;
    LONG    Status;
    LONG    Status2;
    ULONG   Size;

    //
    // Open the NETBT key
    //
    Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                 SubKeyLinkage,
                 0,
                 KEY_READ,
                 &Key);

    if (Status == ERROR_SUCCESS)
    {
        //
        // now read the linkage values
        //
        Status = RegQueryValueEx(Key,
                                 pLinkage,
                                 NULL,
                                 &Type,
                                 NULL,
                                 &Size);
        if (Status != ERROR_SUCCESS)
        {
            DBGPRINT0(ERR, "Error closing the Registry key\n");
            WINSEVT_LOG_M(Status, WINS_EVT_QUERY_NETBT_KEY_ERR);
            (VOID)RegCloseKey(Key);
            return(WINS_FAILURE);
        }
        else
        {
           //
           // Let us allocate a buffer that is big enough to hold all the
           // data
           //
           WinsMscAlloc(Size, (LPVOID *)&pWinsCnfNbtPath);

           //
           // now read the linkage values
           //
           Status = RegQueryValueEx(Key,
                                 pLinkage,
                                 NULL,
                                 &Type,
                                 (LPBYTE)pWinsCnfNbtPath,
                                 &Size);
           Status2 = RegCloseKey(Key);
           if ((Status != ERROR_SUCCESS) || (Status2 != ERROR_SUCCESS))
           {
              DBGPRINT0(ERR, "Error closing the Registry key\n");
              WINSEVT_LOG_M(Status, WINS_EVT_QUERY_NETBT_KEY_ERR);
              return(WINS_FAILURE);
           }
        }
    }
    else
    {
        WINSEVT_LOG_D_M(Status, WINS_EVT_OPEN_NETBT_KEY_ERR);
        return(WINS_FAILURE);
    }

    return(WINS_SUCCESS);
}
#endif

VOID
WinsCnfReadRegInfo(
  PWINSCNF_CNF_T        pWinsCnf
 )

/*++

Routine Description:
        This function is called to read the registry in order to populate the
        WinsCnf structure

Arguments:
        None

Externals Used:
        None

Return Value:
        None

Error Handling:

Called by:
        WinsCnfInitConfig
Side Effects:

Comments:
        None
--*/

{

try {
   if (sfParametersKeyExists)
   {
           /*
                Read in the  registry information pertaining to WINS
           */
           WinsCnfReadWinsInfo(pWinsCnf);
   }

   if (sfPartnersKeyExists)
   {
           //
           // Read the PUSH/PULL records and other global information used for
           // replication
           //
           WinsCnfReadPartnerInfo(pWinsCnf);
   }

   //
   // Do a sanity check on the params. We are not interested in the
   // return code
   //
   (VOID)SanityChkParam(pWinsCnf);
}
except(EXCEPTION_EXECUTE_HANDLER) {
       DBGPRINTEXC("WinsCnfReadRegInfo");

       //
       // If we encountered an exception at boot time, we do not want to
       // reraise the exception, since we want to come up and continue on
       // For the non-initing case, the exception that we raise will be caught
       // in Reinit(). For the boot time case, it is ok to come up with the
       // defaults (an event message is being logged) - in WinsCnf.
       // In the non-init case, the defaults are not in the memory used to
       // read in the parameters (WinsCnf is initialized with stuff in
       // this memory block later).
       //
       if (WinsCnf.State_e != WINSCNF_E_INITING)
       {
             WINS_RERAISE_EXC_M();
       }
       WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_RECONFIG_ERR);
     }

   return;
}


VOID
WinsCnfCopyWinsCnf(
                WINS_CLIENT_E        Client_e,
                PWINSCNF_CNF_T  pSrc
        )

/*++

Routine Description:
        This function is called to copy relevant information from a WINS
        Cnf structure to the master (external) Wins Cnf structure

Arguments:
        pSrc - WinsCnf stucture to copy from

Externals Used:
        WinsCnf


Return Value:

        None
Error Handling:

Called by:
        RplPullInit

Side Effects:

Comments:
        This function may be enhanced in the future

        Note: this function is called only by the main thread
--*/
{

        BOOL fScvParamChg = FALSE;

        if (Client_e == WINS_E_WINSCNF)
        {

FUTURES("Queue a message to the Scavenger thread passing it pSrc")
FUTURES("That will avoid this synchronization overhead")

                //
                // We need to synchronize with the scavenger thread and
                // RPC threads that might be looking at fRplOnlyWCnfPnrs
                //
                EnterCriticalSection(&WinsCnfCnfCrtSec);

                //
                // Also need to synchronize with the nbt threads doing
                // name registrations/refreshes
                //
                EnterCriticalSection(&NmsNmhNamRegCrtSec);

                //
                // Sanity check the parameters
                //
                fScvParamChg = SanityChkParam(pSrc);

                if (fScvParamChg)
                {
                  //
                  // Initialize the scavenging stuff.
                  //
                  WinsCnf.RefreshInterval   = pSrc->RefreshInterval;
                  WinsCnf.TombstoneInterval = pSrc->TombstoneInterval;
                  WinsCnf.TombstoneTimeout  = pSrc->TombstoneTimeout;
                  WinsCnf.CC                = pSrc->CC;
                  WinsCnf.ScvThdPriorityLvl = pSrc->ScvThdPriorityLvl;
                }

                //
                // Store the verify interval since SanityChkParam does
                // not set fScvParamChg if VerifyINterval has changed.
                //
                WinsCnf.VerifyInterval = pSrc->VerifyInterval;

                WinsCnf.fRplOnlyWCnfPnrs     = pSrc->fRplOnlyWCnfPnrs;
                WinsCnf.LogDetailedEvts      = pSrc->LogDetailedEvts;
                WinsCnf.fAdd1Bto1CQueries    = pSrc->fAdd1Bto1CQueries;
                WinsCnf.fPStatic             = pSrc->fPStatic;

                LeaveCriticalSection(&NmsNmhNamRegCrtSec);

                WinsCnf.fDoSpoofing           = pSrc->fDoSpoofing;
                WinsCnf.MaxNoOfWrkThds       = pSrc->MaxNoOfWrkThds;

                WinsCnf.fDoBackupOnTerm      = pSrc->fDoBackupOnTerm;
#if MCAST > 0
                WinsCnf.fUseSelfFndPnrs      = pSrc->fUseSelfFndPnrs;
                WinsCnf.McastIntvl           = pSrc->McastIntvl;
#endif

#if PRSCONN
                WinsCnf.fPrsConn             = pSrc->fPrsConn;
#endif
                if (WinsCnf.fDoBackupOnTerm && pSrc->pBackupDirPath != NULL)
                {
                        if (WinsCnf.pBackupDirPath != NULL)
                        {
                                WinsMscDealloc(WinsCnf.pBackupDirPath);
                        }
                        WinsCnf.pBackupDirPath = pSrc->pBackupDirPath;
                }

                LeaveCriticalSection(&WinsCnfCnfCrtSec);
        //        return;

        }
        else
        {
          if (Client_e == WINS_E_RPLPULL)
          {

                EnterCriticalSection(&WinsCnfCnfCrtSec);
                //
                // Copy the Scavenging parameters into the cnf structure
                // just allocated so that we can compare them for
                // compatibility with the new max. replication time interval
                // (since the Partners key was signaled, the replication
                 // stuff might have changed)
                //
                pSrc->RefreshInterval   = WinsCnf.RefreshInterval;
                pSrc->TombstoneInterval = WinsCnf.TombstoneInterval;
                pSrc->TombstoneTimeout  = WinsCnf.TombstoneTimeout;
                pSrc->VerifyInterval    = WinsCnf.VerifyInterval;

                //
                // Wasteful here and in SanityChkParam
                //
PERF("Pass an argument to SanityChk so that we don't have to do this")
PERF("See similar remark in SanityChk")
                pSrc->CC                = WinsCnf.CC;

                //
                // Sanity check the parameters.
                //
                // Sanity checking of the parameters is done here in
                // the main thread instead of in the PULL thread because
                // we don't want a situation where two different threads
                // (the main thread for changes to the PARAMETERS key)
                // and the PULL thread (for changes in the PARTNERS key)
                // updating the WinsCnf structure
                //
                // Also, as an aside, we don't copy the PullInfo information
                // into WinsCnf here to avoid unnecessary complication and
                // synchronization that would ensue by the fact that we
                // would then have two threads (main thread) and the PULL
                // thread accessing that field (check Reconfig() in
                // rplpull.c).
                //
FUTURES("When we start supporting time interval as an attribute of PUSHing")
FUTURES("move this check inside the NmsNmhNamRegCrtSec below")
                fScvParamChg = SanityChkParam(pSrc);

                //
                // If one or more scavenging parameters have  changed,
                // update WinsCnf and signal the Scavenger thread.
                //
                if (fScvParamChg)
                {
                        WinsCnf.RefreshInterval   = pSrc->RefreshInterval;
                        WinsCnf.TombstoneInterval = pSrc->TombstoneInterval;
                        WinsCnf.TombstoneTimeout  = pSrc->TombstoneTimeout;

                        //
                        // If SanityChkParam changed Tombstone interval, then
                        // verify interval has also changed.
                        //
                        WinsCnf.VerifyInterval = pSrc->VerifyInterval;

                }

                WinsCnf.MaxRplTimeInterval = pSrc->MaxRplTimeInterval;
                WinsCnf.RplType              = pSrc->RplType;
                LeaveCriticalSection(&WinsCnfCnfCrtSec);
          }
        }

        //
        // If the scavenging params have changed we need to signal
        // the scavenger thread
        //
        if (fScvParamChg)
        {
                WinsMscSignalHdl(WinsCnf.CnfChgEvtHdl);
        }
        return;
}


LPVOID
WinsCnfGetNextRplCnfRec(
         PRPL_CONFIG_REC_T        pCnfRec,
        RPL_REC_TRAVERSAL_E        RecTrv_e
        )

/*++

Routine Description:
        This function is called to get to the next configuration record

Arguments:
        pCnfRec - The current configuration record in a buffer of configuration
                  records
        RecTrv_e - indicates how the next one should be retrieved.  If set to
                  TRUE, it means that the next record to be retrieved is one
                  that follows the current record in the buffer.  If set to
                  FALSE, the next record is retrieved using the pNext field
                  of the current configuration record



Externals Used:
        None

Return Value:
        address of the next configuration record

Error Handling:

Called by:
        EstablishComm in rplpull.c, WinsPushTrigger() in wins.c

Side Effects:

Comments:
        None
--*/

{
        //
        // If no traversal is desired, return NULL as the next record
        //
        if (RecTrv_e == RPL_E_NO_TRAVERSAL)
        {
                return(NULL);
        }
        //
        //  Go to the next configuration record in a way specified
        //  by the  value of the RecTrv_e flag.
        //
        if(RecTrv_e == RPL_E_IN_SEQ)
        {
                pCnfRec = (PRPL_CONFIG_REC_T)(
                                 (LPBYTE)pCnfRec + RPL_CONFIG_REC_SIZE);
        }
        else  // RPL_E_VIA_LINK
        {
                return(pCnfRec->pNext);
        }
        return(pCnfRec);
}


VOID
WinsCnfAskToBeNotified(
         WINSCNF_KEY_E        KeyToMonitor_e
        )

/*++

Routine Description:
        This function is called to request that WINS be notified when
        the information pertaining to WINS and its subkeys in the registry
        changes

Arguments:

        KeyToMonitor_e

Externals Used:
        None


Return Value:

        None

Error Handling:

Called by:
        Reinit() in nms.c
        WinsCnfOpenSubKeys()
        WinsCnfInitConfig()

Side Effects:

Comments:
        None
--*/

{
   DWORD  NotifyFilter = 0;
   LONG          RetVal;
   DWORD  Error;

#define  CHK_RET_VAL_M     {                                            \
                                if (RetVal != ERROR_SUCCESS)         \
                                   {                                     \
                                        DBGPRINT1(ERR, "WinsAskToBeNotified: Error = (%d)\n", RetVal);                                                \
                                        WINSEVT_LOG_M(                      \
                                                 WINS_FATAL_ERR,              \
                                                 WINS_EVT_REG_NTFY_FN_ERR \
                                                      );             \
                                   }                                    \
                           }

   /*
    *  Set the notify filter.  Ask to be notified for all changes.
    */
   NotifyFilter = REG_NOTIFY_CHANGE_NAME       |
                  REG_NOTIFY_CHANGE_ATTRIBUTES |
                  REG_NOTIFY_CHANGE_LAST_SET  |
                  REG_NOTIFY_CHANGE_SECURITY ;


   switch(KeyToMonitor_e)
   {
        case(WINSCNF_E_WINS_KEY):

//                DBGPRINT0(SPEC, "WinsCnfAskToBeNotified: WINS Key\n");
                   RetVal = RegNotifyChangeKeyValue(
                            sConfigRoot,
                            TRUE,        //report changes in key and all subkeys
                            REG_NOTIFY_CHANGE_NAME,
                            WinsCnf.WinsKChgEvtHdl,
                            TRUE         //Async signaling is what we want
                           );
                CHK_RET_VAL_M;
                break;
        case(WINSCNF_E_PARAMETERS_KEY):

//                DBGPRINT0(SPEC, "WinsCnfAskToBeNotified: PARAMETERS Key\n");
                   RetVal = RegNotifyChangeKeyValue(
                            sParametersKey,
                            TRUE,        //report changes in key and all subkeys
                            NotifyFilter,
                            WinsCnf.ParametersKChgEvtHdl,
                            TRUE         //Async signaling is what we want
                           );
                CHK_RET_VAL_M;
                break;

        case(WINSCNF_E_PARTNERS_KEY):

//                DBGPRINT0(SPEC, "WinsCnfAskToBeNotified: PARTNERS Key\n");
                   RetVal = RegNotifyChangeKeyValue(
                            sPartnersKey,
                            TRUE,        //report changes in key and all subkeys
                            NotifyFilter,
                            WinsCnf.PartnersKChgEvtHdl,
                            TRUE         //Async signaling is what we want
                           );

                CHK_RET_VAL_M;
                break;

FUTURES("Remove the following case")
        //
        // The following case would never get exercised.
        //
        case(WINSCNF_E_ALL_KEYS):

                   RetVal = RegNotifyChangeKeyValue(
                            sConfigRoot,
                            TRUE,        //report changes in key and all subkeys
                            REG_NOTIFY_CHANGE_NAME,
                            WinsCnf.WinsKChgEvtHdl,
                            TRUE         //Async signaling is what we want
                           );
                CHK_RET_VAL_M;
                if (sfParametersKeyExists)
                {
                           RetVal = RegNotifyChangeKeyValue(
                                            sParametersKey,
                                            TRUE,        //report changes in key and
                                                //all subkeys
                                            NotifyFilter,
                                            WinsCnf.ParametersKChgEvtHdl,
                                            TRUE         //Async signaling is what we
                                                 // want
                                                           );
                        if (RetVal != ERROR_SUCCESS)
                        {
                                Error = GetLastError();
                                if (Error == ERROR_BADKEY)
                                {
                                        //
                                        // Key must not be there
                                        //
                                        sfParametersKeyExists = FALSE;
                                }
                                else
                                {
                                        DBGPRINT1(ERR,
        "WinsCnfAskToBeNotified: RegNotifyChangeKeyValue error = (%d)\n",
                                                 Error);

                                }
                        }
                }
                if (sfPartnersKeyExists)
                {
                           RetVal = RegNotifyChangeKeyValue(
                                            sPartnersKey,
                                            TRUE,        //report changes in key and
                                                //all subkeys
                                            NotifyFilter,
                                            WinsCnf.PartnersKChgEvtHdl,
                                            TRUE         //Async signaling is what we
                                                 //want
                                                           );
                        if (RetVal != ERROR_SUCCESS)
                        {
                                Error = GetLastError();
                                if (Error == ERROR_BADKEY)
                                {
                                        //
                                        // Key must not be there
                                        //
                                        sfPartnersKeyExists =  FALSE;
                                }
                                else
                                {
                                        DBGPRINT1(ERR,
        "WinsCnfAskToBeNotified: RegNotifyChangeKeyValue error = (%d)\n",
                                                 Error);

                                }
                        }
                }
                        break;
        default:

                DBGPRINT1(ERR, "WinsCnfAskToBeNotified: Wrong Hdl (%d)\n",
                                KeyToMonitor_e);
                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
                WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
                break;
        }


            return;
}

VOID
WinsCnfDeallocCnfMem(
  PWINSCNF_CNF_T        pWinsCnf
        )

/*++

Routine Description:
        This function is called to deallocate the Wins Cnf structure and
        memory associated with it

Arguments:


Externals Used:
        None


Return Value:
        None

Error Handling:

Called by:
        Reconfig in rplpull.c

Side Effects:

Comments:
        None
--*/

{
try {
        //
        // Deallocate the buffer holding one or more names of files used
        // for STATIC initialization of WINS
        //
        if (pWinsCnf->pStaticDataFile != NULL)
        {
                WinsMscDealloc(pWinsCnf->pStaticDataFile);
        }
        WinsMscDealloc(pWinsCnf);
}
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsCnfDeallocCnfMem");
        DBGPRINT0(EXC, "WinsCnfDeallocCnfMem: Got an exception\n");
        WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_RECONFIG_ERR);
        }

        return;
}

VOID
GetKeyInfo(
        IN  HKEY                   Key,
        IN  WINSCNF_KEY_E        KeyType_e,
        OUT LPDWORD                  pNoOfSubKeys,
        OUT LPDWORD                pNoOfVals
        )

/*++

Routine Description:
        This function is called to get the number of subkeys under a key

Arguments:
        Key         - Key whose subkey count has to be determined
        KeyType_e
        pNoOfSubKeys

Externals Used:
        None


Return Value:

        None
Error Handling:

Called by:
        GetPnrInfo()

Side Effects:

Comments:
        None
--*/

{
          TCHAR    ClsStr[40];
          DWORD    ClsStrSz = sizeof(ClsStr);
          DWORD    LongestKeyLen;
          DWORD    LongestKeyClassLen;
          DWORD    LongestValueNameLen;
          DWORD    LongestValueDataLen;
          DWORD    SecDesc;
        LONG         RetVal;

          FILETIME LastWrite;
          /*
                Query the key.  The subkeys are IP addresses of PULL
                partners.
          */
          RetVal = RegQueryInfoKey(
                        Key,
                        ClsStr,
                        &ClsStrSz,
                        NULL,                        //must be NULL, reserved
                        pNoOfSubKeys,
                        &LongestKeyLen,
                        &LongestKeyClassLen,
                        pNoOfVals,
                        &LongestValueNameLen,
                        &LongestValueDataLen,
                        &SecDesc,
                        &LastWrite
                                );

          if (RetVal != ERROR_SUCCESS)
          {
                WINSEVT_LOG_M(
                        WINS_FATAL_ERR,
                        KeyType_e == WINSCNF_E_DATAFILES_KEY ?
                                WINS_EVT_CANT_QUERY_DATAFILES_KEY :
                                ((KeyType_e == WINSCNF_E_PULL_KEY) ?
                                        WINS_EVT_CANT_QUERY_PULL_KEY :
                                        ((KeyType_e == WINSCNF_E_PUSH_KEY) ?
                                        WINS_EVT_CANT_QUERY_PUSH_KEY :
                                        WINS_EVT_CANT_QUERY_SPEC_GRP_MASKS_KEY))
                             );
                WINS_RAISE_EXC_M(WINS_EXC_CANT_QUERY_KEY);
        }
        return;
}
VOID
WinsCnfOpenSubKeys(
        VOID
        )

/*++

Routine Description:
        This function opens the subkeys of the WINS key.  The subkeys are
        the PARTNERS key and the PARAMETERS key.
Arguments:
        None

Externals Used:
        sfParamatersKeyExists
        sfPartnersKeyExists

Return Value:
        None

Error Handling:

Called by:

        WinsCnfInitConfig()

Side Effects:

Comments:
        None
--*/

{

   LONG  RetVal;

   //
   // Check if the Parameters and Partners Keys are present
   //
   ChkWinsSubKeys();

   //
   // Try to open the Parameters key if it exists
   //
   if ((sfParametersKeyExists) && (!sfParametersKeyOpen))
   {
           /*
           *  Open the Parameters key
           */
           RetVal =   RegOpenKeyEx(
                        sConfigRoot,                //predefined key value
                        _WINS_CFG_PARAMETERS_KEY,
                        0,                        //must be zero (reserved)
                        KEY_READ | KEY_WRITE,        //we desire read/write access
                                                // to the key
                        &sParametersKey                //handle to key
                                );

           if (RetVal != ERROR_SUCCESS)
           {
CHECK("Is there any need to log this")
                WINSEVT_LOG_INFO_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_OPEN_PARAMETERS_KEY
                                   );
                sfParametersKeyExists = FALSE;
           }
           else
           {
                sfParametersKeyOpen = TRUE;
                WinsCnfAskToBeNotified(WINSCNF_E_PARAMETERS_KEY);
           }
   }

   //
   // Try to open the Partners key if it exists
   //
   if ((sfPartnersKeyExists) && (!sfPartnersKeyOpen))
   {
           /*
           *  Open the Partners key
           */
           RetVal =   RegOpenKeyEx(
                                sConfigRoot,                //predefined key value
                                _WINS_CFG_PARTNERS_KEY,
                                0,                        //must be zero(reserved)
                                KEY_READ,                //we desire read
                                                        //access to the key
                                &sPartnersKey                //handle to key
                                );

           if (RetVal != ERROR_SUCCESS)
           {

CHECK("Is there any need to log this")
                WINSEVT_LOG_INFO_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_OPEN_KEY
                         );
                sfPartnersKeyExists = FALSE;
           }
           else
           {
                sfPartnersKeyOpen = TRUE;
                WinsCnfAskToBeNotified(WINSCNF_E_PARTNERS_KEY);
           }
   }

   return;

}  //WinsCnfOpenSubKeys()

BOOL
SanityChkParam(
        PWINSCNF_CNF_T        pWinsCnf
        )

/*++

Routine Description:
        This function  is called to ensure that the time intervals for
        scavenging specified in WinsCnf are compatible with the ones
        used for replication

Arguments:
        pWinsCnf - ptr to the WINS configuration

Externals Used:
        None


Return Value:
        None

Error Handling:

Called by:
        WinsCnfCopyWinsCnf (during reinitialization of the WINS), by
        WinsCnfReadRegInfo() during initialization of the WINS
Side Effects:

        The scavenging intervals could be affected

Comments:
        This function must be called from inside the critical section
        guarded by WinsCnfCnfCrtSec except at process initialization.
--*/
{
        DWORD   MinTombInterval;
        BOOL        fScvParamChg = FALSE;
        WINSEVT_STRS_T  EvtStr;
        EvtStr.NoOfStrs = 1;

        DBGENTER("SanityChkParam\n");

        //
        // Get the minimum tombstone time interval
        //
        MinTombInterval = WINSCNF_MAKE_TOMB_INTVL_M(pWinsCnf->RefreshInterval,
                                           pWinsCnf->MaxRplTimeInterval);

        //
        // Make the actual equal to the min. if it is less
        //
        if (!sfNoLimitChk && (pWinsCnf->TombstoneInterval < MinTombInterval))
        {
                DBGPRINT2(FLOW, "SanityChkParam: Adjusting Tombstone Interval from (%d) to (%d)\n", pWinsCnf->TombstoneInterval, MinTombInterval);

FUTURES("This is actually a warning. Use a different macro or enhance it")
FUTURES("Currently, it will log this message as an informational")

                WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL_R,
                             NULL, __LINE__, "ud",
                             WINSCNF_TOMBSTONE_INTVL_NM,
                             MinTombInterval );

                pWinsCnf->TombstoneInterval = MinTombInterval;

                //
                // Verify Interval is dependent on the tombstone interval
                //
                pWinsCnf->VerifyInterval = WINSCNF_MAKE_VERIFY_INTVL_M(MinTombInterval);
                fScvParamChg = TRUE;
        }

        //
        // reusing the var. The time interval is for tombstone timeout
        //
        MinTombInterval =
                  WINSCNF_MAKE_TOMBTMOUT_INTVL_M(pWinsCnf->MaxRplTimeInterval);
        if (!sfNoLimitChk && (pWinsCnf->TombstoneTimeout <  MinTombInterval))
        {
                DBGPRINT2(FLOW, "SanityChkParam: Adjusting Tombstone Timeout from (%d) to (%d)\n", pWinsCnf->TombstoneInterval, MinTombInterval);

                pWinsCnf->TombstoneTimeout = MinTombInterval;
                WinsEvtLogDetEvt(TRUE, WINS_EVT_ADJ_TIME_INTVL_R,
                             NULL, __LINE__, "ud",
                             WINSCNF_TOMBSTONE_TMOUT_NM,
                             MinTombInterval );
                if (!fScvParamChg)
                {
                        fScvParamChg = TRUE;
                }

        }
        if (!fScvParamChg)
        {
PERF("Pass an argument to SanityChk so that we don't have to do this")
PERF("for CC for the case where we just read the partner info. See")
PERF("WinsCnfCopyWinsCnf for the case where client_e is WINS_E_RPLPULL")
                if (
                     (WinsCnf.RefreshInterval != pWinsCnf->RefreshInterval)
                                        ||
                     (WinsCnf.TombstoneInterval != pWinsCnf->TombstoneInterval)
                                        ||
                     (WinsCnf.TombstoneTimeout != pWinsCnf->TombstoneTimeout)
                                        ||
                     (WinsCnf.CC.TimeInt  != pWinsCnf->CC.TimeInt)
                                        ||
                     (WinsCnf.CC.SpTimeInt  != pWinsCnf->CC.SpTimeInt)
                                        ||
                     (WinsCnf.CC.fUseRplPnrs  != pWinsCnf->CC.fUseRplPnrs)
                                        ||
                     (WinsCnf.CC.MaxRecsAAT  != pWinsCnf->CC.MaxRecsAAT)
                   )
                {
                        fScvParamChg = TRUE;
                }
        }

        DBGLEAVE("SanityChkParam\n");
        return(fScvParamChg);
}
STATUS
WinsCnfGetNamesOfDataFiles(
        PWINSCNF_CNF_T        pWinsCnf
        )

/*++

Routine Description:
        This function gets the names of all the datafiles that need to
        be used for initializing WINS.

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --  WINS_SUCCESS
   Error status codes   --  WINS_FAILURE

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{

        LONG             RetVal;
        HKEY             DFKey;
        DWORD            BuffSize;
        STATUS          RetStat = WINS_SUCCESS;
        PWINSCNF_DATAFILE_INFO_T            pSaveDef;
        DWORD          NoOfSubKeys;

        DBGENTER("WinsCnfGetNamesOfDataFiles\n");

        //
        // Store timestamp of initialization in the statistics structure
        //
        WinsIntfSetTime(NULL, WINSINTF_E_INIT_DB);

        //
        // Set up the default name
        //

        //
        // First allocate the buffer that will hold the default file name
        //
        WinsMscAlloc(WINSCNF_FILE_INFO_SZ, &pWinsCnf->pStaticDataFile);

        lstrcpy(pWinsCnf->pStaticDataFile->FileNm, WINSCNF_STATIC_DATA_NAME);

        //
        // The default name contains a %<string>% in it.  Therefore, specify
        // the type as EXPAND_SZ
        //
        pWinsCnf->pStaticDataFile->StrType = REG_EXPAND_SZ;
        pWinsCnf->NoOfDataFiles            = 1;

        pSaveDef = pWinsCnf->pStaticDataFile;  //save the address

           /*
           *  Open the DATAFILES key
           */
           RetVal =   RegOpenKeyEx(
                        sConfigRoot,                //predefined key value
                        _WINS_CFG_DATAFILES_KEY,
                        0,                //must be zero (reserved)
                        KEY_READ,        //we desire read access to the keyo
                        &DFKey                //handle to key
                );

           if (RetVal != ERROR_SUCCESS)
           {

CHECK("Is there any need to log this")
                WINSEVT_LOG_INFO_M(
                                WINS_SUCCESS,
                                WINS_EVT_CANT_OPEN_DATAFILES_KEY
                                 );
                DBGLEAVE("WinsCnfGetNamesOfDataFiles\n");
                return(FALSE);
           }
        else
try {
        {
                //
                // Get the count of data files listed under the DATAFILES
                // key
                //
                GetKeyInfo(
                        DFKey,
                        WINSCNF_E_DATAFILES_KEY,
                        &NoOfSubKeys,                        //ignored
                        &pWinsCnf->NoOfDataFiles
                      );
        }
        if (pWinsCnf->NoOfDataFiles > 0)
        {

                DWORD                          Index;
                PWINSCNF_DATAFILE_INFO_T pTmp;
                TCHAR ValNmBuff[MAX_PATH];
                DWORD ValNmBuffSz = MAX_PATH;


                  //
                  // Allocate buffer big enough to hold data for
                // the number of subkeys found under the PULL key
                  //
                  BuffSize = WINSCNF_FILE_INFO_SZ * pWinsCnf->NoOfDataFiles;
                    WinsMscAlloc( BuffSize, &pWinsCnf->pStaticDataFile);

                   /*
                    *   Enumerate  the values
                     */
                     for(
                        Index = 0, pTmp = pWinsCnf->pStaticDataFile;
                        Index <  pWinsCnf->NoOfDataFiles;
                                // no third expression
                         )
                {
                        ValNmBuffSz = sizeof(ValNmBuff)/sizeof(TCHAR);  //init before
                                                          //every call
                        BuffSize  = sizeof(pWinsCnf->pStaticDataFile->FileNm);
                          RetVal = RegEnumValue(
                                    DFKey,
                                    Index,        //key
                                    ValNmBuff,
                                    &ValNmBuffSz,
                                    (LPDWORD)NULL,                //reserved
                                    &pTmp->StrType,
                                    (LPBYTE)(pTmp->FileNm),//ptr to var. to
                                                           //hold name of
                                                           //datafile
                                    &BuffSize        //not looked at by us
                                            );

                        if (RetVal != ERROR_SUCCESS)
                        {
                                continue;
                        }
                        //
                        // if StrType is not REG_SZ or REG_EXPAND_SZ, go to
                        // the next  Value
                        //
                        if  (
                                (pTmp->StrType != REG_EXPAND_SZ)
                                        &&
                                   (pTmp->StrType != REG_SZ)
                                )
                        {
                                continue;
                        }

                        Index++;
                        pTmp = (PWINSCNF_DATAFILE_INFO_T)((LPBYTE)pTmp +
                                                WINSCNF_FILE_INFO_SZ);
                }

                //
                // If not even one valid name was retrieved, get rid of the
                // buffer
                //
                if (Index == 0)
                {
                        //
                        // Get rid of the buffer
                        //
                        WinsMscDealloc((LPBYTE)pWinsCnf->pStaticDataFile);

                        //
                        // We will use the default
                        //
                        pWinsCnf->pStaticDataFile = pSaveDef;
                }
                else
                {
                        //
                        // Get rid of the default name buffer
                        //
                        WinsMscDealloc((LPBYTE)pSaveDef);
                }

                pWinsCnf->NoOfDataFiles = Index;
        }
 } // end of try ..
except (EXCEPTION_EXECUTE_HANDLER) {
                DBGPRINTEXC("WinsCnfGetNamesOfDataFiles");
                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
                RetStat = WINS_FAILURE;
        }
         REG_M(
                RegCloseKey(DFKey),
                WINS_EVT_CANT_CLOSE_KEY,
                WINS_EXC_CANT_CLOSE_KEY
             );
        DBGLEAVE("WinsCnfGetNamesOfDataFiles\n");
        return(RetStat);
}



VOID
WinsCnfCloseKeys(
        VOID
        )

/*++

Routine Description:
        This function closes the the open keys.  The keys closed are
        the WINS key, the PARTNERS key, and the PARAMETERS key.
Arguments:
        None

Externals Used:
        sfParametersKeyExists
        sfPartnersKeyExists

Return Value:
        None

Error Handling:

Called by:
        Reinit()

Side Effects:

Comments:
        We don't look at the return code of RegCloseKey.  This is because
        we might call this function even with the key not being open (not
        the case currently).

--*/

{

   //
   // Close the PARAMETERS key if it  is open
   //
   if (sfParametersKeyOpen)
   {
           (VOID)RegCloseKey(sParametersKey);
   }

   //
   // Close the PARTNERS key if it is open
   //
   if (sfPartnersKeyOpen)
   {
           (VOID)RegCloseKey(sPartnersKey);
   }

#if 0
   //
   // NOTE NOTE NOTE: Build 436.  If we attempt to close a key that has been
   // deleted from the registry NT comes down
   //

   //
   // Close the WINS key
   //
   (VOID)RegCloseKey(sConfigRoot);
#endif

   return;
}  //WinsCnfCloseKeys()


VOID
ChkWinsSubKeys(
        VOID
        )

/*++

Routine Description:
        This function is called to check whether we have the PARTNERS
        and PARAMETERS sub-keys under the root subkey of WINS.

Arguments:
        None

Externals Used:
        None

Return Value:
        None

Error Handling:

Called by:
        Reinit() in nms.c

Side Effects:

Comments:
        None
--*/

{
        DWORD      NoOfSubKeys = 0;
          DWORD             KeyNameSz;
          TCHAR           KeyName[20];
          FILETIME   LastWrite;
        LONG           RetVal;
        BOOL           fParametersKey = FALSE;
        BOOL           fPartnersKey   = FALSE;

           /*
            *   Get each subkey's  name
           */
            RetVal = ERROR_SUCCESS;
            for(
                        ;
                RetVal == ERROR_SUCCESS;
                NoOfSubKeys++
            )
          {
                KeyNameSz = sizeof(KeyName)/sizeof(TCHAR);  //init before every call
                 RetVal = RegEnumKeyEx(
                                sConfigRoot,
                                NoOfSubKeys,        //key
                                KeyName,
                                &KeyNameSz,
                                NULL,                //reserved
                                NULL,                //don't need class name
                                NULL,                //ptr to var. to hold class name
                                &LastWrite        //not looked at by us
                                );

                if (RetVal != ERROR_SUCCESS)
                {
                        continue;
                }

                if (lstrcmp(KeyName, _WINS_CFG_PARAMETERS_KEY) == 0)
                {
                        fParametersKey = TRUE;
                }

                if (lstrcmp(KeyName, _WINS_CFG_PARTNERS_KEY) == 0)
                {
                        fPartnersKey = TRUE;
                }
        }

        //
        // if the Parameters key does not exist but it existed before,
        // close the key to get rid of the handle we have
        //
        if (!fParametersKey)
        {
                 if (sfParametersKeyExists)
                {
                        sfParametersKeyExists = FALSE;
                        sfParametersKeyOpen = FALSE;
                }
        }
        else
        {
                sfParametersKeyExists = TRUE;
        }

        //
        // if the Partners key does not exist but it existed before,
        // close the key to get rid of the handle we have
        //
        if (!fPartnersKey)
        {
                if (sfPartnersKeyExists)
                {
                        sfPartnersKeyExists = FALSE;
                        sfPartnersKeyOpen   = FALSE;
                }
        }
        else
        {
                sfPartnersKeyExists = TRUE;
        }

        return;
} //ChkWinsSubKeys()

VOID
GetSpTimeData(
        HKEY              SubKey,
        LPSYSTEMTIME      pCurrTime,
        LPBOOL            pfSpTime,
        LPDWORD           pSpTimeIntvl

/*++

Routine Description:
        This function is called to get the specific time and period information
        for a PULL/PUSH record.

Arguments:
        SubKey   - Key of a WINS under the Pull/Push key
        pCnfRFec - ptr to the Conf. record of the WINS

Externals Used:
        None


Return Value:

   Success status codes --  WINS_SUCCESS
   Error status codes   --  WINS_NO_SP_TIME

Error Handling:

Called by:
        GetPnrInfo

Side Effects:

Comments:
        None
--*/

        )
{
        DWORD    ValTyp;
        BYTE     tSpTime[MAX_SZ_SIZE];
        BYTE     SpTime[MAX_SZ_SIZE];
        LPBYTE   pSpTime = SpTime;
        DWORD    Sz = sizeof(tSpTime);
        LONG     RetVal;
        DWORD    Hr = 0;
        DWORD    Mt = 0;
        DWORD    Sec = 0;
        LONG     TimeInt;


   // DBGENTER("GetSpTimeData\n");
        *pfSpTime = FALSE;

try {

            Sz = sizeof(tSpTime);
            RetVal = RegQueryValueEx(
                             SubKey,
                             WINSCNF_SP_TIME_NM,
                             NULL,        //reserved; must be NULL
                             &ValTyp,
                             tSpTime,
                             &Sz
                                                );

            //
            // If the user has not specifed a specific time, then we use
            // the current time as the specific time.  For current time,
            // the interval is 0
            //
            if (RetVal == ERROR_SUCCESS)
            {

#ifdef UNICODE
                (VOID)WinsMscConvertUnicodeStringToAscii(tSpTime, SpTime, MAX_SZ_SIZE);
#else
                pSpTime = tSpTime;
#endif

                RetVal = (LONG)sscanf(pSpTime, "%d:%d:%d", &Hr, &Mt, &Sec);
                if ((RetVal == EOF) || (RetVal == 0))
                {
                        DBGPRINT1(ERR, "GetSpTime: Wrong time format (%s)\n",
                                                pSpTime);
                        WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WRONG_TIME_FORMAT);
                }
                else
                {

                  *pSpTimeIntvl = 0;
                  if ((Hr <= 23)  && (Mt <= 59) && (Sec <= 59))
                  {
                     TimeInt =  ((Hr * 3600) + (Mt * 60) + Sec) -
                                       ((pCurrTime->wHour * 3600) +
                                        (pCurrTime->wMinute * 60) +
                                         pCurrTime->wSecond);
                     if (TimeInt < 0)
                     {
                        *pSpTimeIntvl =  (24 * 3600) + TimeInt;
                     }
                     else
                     {
                        *pSpTimeIntvl =  TimeInt;
                     }
                     *pfSpTime      = TRUE;
                     DBGPRINT1(DET, "GetSpTimeData: Sp. Time Interval is %d\n",
                            *pSpTimeIntvl);
                  }
                  else
                  {
                     DBGPRINT0(DET, "GetSpTimeData: WRONG TIME FORMAT\n");
                  }
                }
            }
 }
 except (EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("GetSpTime");
        WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_CONFIG_ERR);
        }
//    DBGLEAVE("GetSpTimeData\n");
    return;
}
#if MCAST > 0
STATUS
WinsCnfAddPnr(
  RPL_RR_TYPE_E  RRType_e,
  LPBYTE         pPnrAdd
)
/*++

Routine Description:


Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{

     LONG RetVal;
     HKEY  CnfKey;
     HKEY  PnrKey;
     DWORD NewKeyInd;

     DBGENTER("WinsCnfAddPnr\n");
     /*
     *  Open the key (PULL/PUSH)
     */
     RetVal =   RegOpenKeyEx(
                sConfigRoot,                //predefined key value
                RRType_e == RPL_E_PULL ?
                        _WINS_CFG_PULL_KEY :
                        _WINS_CFG_PUSH_KEY,        //subkey for WINS
                0,                          //must be zero (reserved)
                KEY_CREATE_SUB_KEY,    //we want "subkey create" priv
                &CnfKey                //handle to key
                );

     if (RetVal != ERROR_SUCCESS)
     {

CHECK("Is there any need to log this")
        WINSEVT_LOG_INFO_M(
                                RetVal,
                                RRType_e == RPL_E_PULL ?
                                        WINS_EVT_CANT_OPEN_PULL_KEY :
                                        WINS_EVT_CANT_OPEN_PUSH_KEY
                         );
        return (WINS_FAILURE);
   }
     //
     //  Add the pnr
     //
     RetVal = RegCreateKeyExA(
                CnfKey,        //predefined key value
                pPnrAdd,                //subkey for WINS
                0,                        //must be zero (reserved)
                "Class",                //class -- may change in future
                REG_OPTION_NON_VOLATILE, //non-volatile information
                KEY_ALL_ACCESS,                //we desire all access to the keyo
                NULL,                         //let key have default sec. attributes
                &PnrKey,                //handle to key
                &NewKeyInd                //is it a new key (out arg)
                );



    if (RetVal != ERROR_SUCCESS)
    {
        WINSEVT_LOG_M(
                       RetVal,
                       RRType_e == RPL_E_PULL ? WINS_EVT_CANT_OPEN_PULL_SUBKEY
                                              : WINS_EVT_CANT_OPEN_PUSH_SUBKEY
                           );
         DBGPRINT3(ERR, "WinsCnfAddPnr: Could not create key with address = (%s) under the %s Key. RetVal = (%d)\n", pPnrAdd,
                       RRType_e == RPL_E_PULL ? "PULL" : "PUSH",
                       RetVal);
         return(WINS_FAILURE);
    }
    else
    {
         if (NewKeyInd != REG_CREATED_NEW_KEY)
         {
            DBGPRINT2(ERR, "WinsCnfAddPnr: key with address = (%s) under the %s Key already present\n", pPnrAdd, RRType_e == RPL_E_PULL ? "PULL" : "PUSH");
         }
         else
         {

             //
             // If Pull pnr, add the time interval
             //
             if (RRType_e == RPL_E_PULL)
             {
                 //
                 // Add the time interval
                 //
                 SetVal(PnrKey, WINSCNF_RPL_INTERVAL_NM, REG_DWORD,
                     (LPWSTR)WINSCNF_TIME_INT_W_SELF_FND_PNRS, sizeof(DWORD));

             }
             SetVal(PnrKey, WINSCNF_SELF_FND_NM, REG_DWORD,
                       (LPWSTR)TRUE, sizeof(DWORD));

         }
         RegCloseKey(PnrKey);
         RegCloseKey(CnfKey);

    }

    DBGLEAVE("WinsCnfAddPnr\n");
    return(WINS_SUCCESS);
}
STATUS
WinsCnfDelPnr(
  RPL_RR_TYPE_E  RRType_e,
  LPBYTE         pPnrAdd
)
/*++

Routine Description:


Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{

     LONG RetVal;
     HKEY  TypeOfPnrKey;
     HKEY  PnrKey;
     //WCHAR Key[160];
     WCHAR String[160];
     BOOL  fSelfFnd;
     DWORD Sz;
     DWORD ValType;

     DBGENTER("WinsCnfDelPnr\n");

     WinsMscConvertAsciiStringToUnicode(pPnrAdd, (LPBYTE)String, sizeof(String)/sizeof(WCHAR));

     /*
     *  Open the key (PULL/PUSH)
     */
     RetVal =   RegOpenKeyEx(
                sConfigRoot,                //predefined key value
                RRType_e == RPL_E_PULL ?
                        _WINS_CFG_PULL_KEY :
                        _WINS_CFG_PUSH_KEY,
                0,                          //must be zero (reserved)
                KEY_ALL_ACCESS,
                &TypeOfPnrKey                //handle to key
                );

     if (RetVal != ERROR_SUCCESS)
     {

CHECK("Is there any need to log this")
        WINSEVT_LOG_INFO_M(
                                WINS_SUCCESS,
                                RRType_e == RPL_E_PULL ?
                                        WINS_EVT_CANT_OPEN_PULL_KEY :
                                        WINS_EVT_CANT_OPEN_PUSH_KEY
                         );
        //--ft: Prefix bug 444974 - this key has to exist. If absurdly is missing, we won't find
        // the partner anyhow.
        return (WINS_SUCCESS);
     }
     RetVal = RegOpenKeyEx(
                TypeOfPnrKey,        //predefined key value
                String,
                0,                          //must be zero (reserved)
                KEY_ALL_ACCESS,    //we want "subkey create" priv
                &PnrKey                //handle to key
                );



    if (RetVal != ERROR_SUCCESS)
    {
         DBGPRINT3(ERR, "WinsCnfDelPnr: %s Pnr with address = (%s) is Non-existent. RetVal = (%d)",
                       RRType_e == RPL_E_PULL ? "PULL" : "PUSH", pPnrAdd,
                       RetVal);
         return(WINS_SUCCESS);
    }
    else
    {
          Sz = sizeof(fSelfFnd);
          RetVal = RegQueryValueEx(
                                        PnrKey,
                                        WINSCNF_SELF_FND_NM,
                                        NULL,
                                        &ValType,
                                        (LPBYTE)&fSelfFnd,
                                        &Sz
                                  );

         //
         // If SelfFnd is there and it's value is 1, delete it
         //
         if ((RetVal == ERROR_SUCCESS) && (fSelfFnd == 1))
         {
            RetVal = RegDeleteKey(TypeOfPnrKey, String);
            if (RetVal != ERROR_SUCCESS)
            {
               DBGPRINT3(ERR, "WinsCnfDelPnr: Could not delete %s Pnr with address = (%s). RetVal = (%d)",
                       RRType_e == RPL_E_PULL ? "PULL" : "PUSH", pPnrAdd,
                       RetVal);
               RegCloseKey(PnrKey);
               return(WINS_FAILURE);

            }
         }
         RegCloseKey(TypeOfPnrKey);

    }

    DBGLEAVE("WinsCnfDelPnr\n");
    return(WINS_SUCCESS);
}

DWORD
SetVal(
       HKEY     RootKey,
       LPWSTR   pName,
       DWORD    ValType,
       LPWSTR   pVal,
       DWORD    ValSize
)
{
        UINT Status = WINS_SUCCESS;
        LONG  RetVal;
        DWORD Val;
        if (ValType == REG_DWORD)
        {
             Val = PtrToUlong (pVal);
        }
        RetVal = RegSetValueEx(
                          RootKey,
                          pName,
                          0,         //reserved -- must be 0
                          ValType,
                          ValType == REG_DWORD ? (LPBYTE)&Val : (LPBYTE)pVal,
                          ValType == REG_DWORD ?  ValSize : lstrlen(pVal)
                             );

        if (RetVal != ERROR_SUCCESS)
        {
                 printf("SetVal: Could not set value of %s\n", pName);
                 Status = WINS_FAILURE;
        }

        return(Status);
}
#endif

DWORD
WinsCnfWriteReg(
    LPVOID  pTmp
    )

/*++

Routine Description:
    This function write the value of the version counter to be used
    at the next invocation.

Arguments:
    pTmp - Will be NULL if WinsCnfNextTimeVersNo was found in the registry
           when WINS came up.

Externals Used:
    NmsHighWaterMarkVersNo
    NmsVersNoToStartFromNextTime
    NmsNmhNamRegCrtSec
    NmsRangeSize
    NmsHalfRangeSize
    sfVersNoChanged
    sfVersNoUpdThdExists

Return Value:

    VOID

Error Handling:

Called by:
    NMSNMH_INC_VERS_COUNTER_M

Side Effects:

Comments:
        None
--*/

{
    LONG  RetVal;
    LONG  RetVal2;
    VERS_NO_T VersNo;
    DBGENTER("WinsCnfWriteReg\n");
    EnterCriticalSection(&NmsNmhNamRegCrtSec);

    //
    // if pTmp is not NULL, it means that either WINS did not find
    // Next time's version number in the registry or that the max. version
    // number in the db is greater than the high water mark we set at
    // initialization.  In the former case, we already have the correct
    // value in NmsNmhToStartFromNextTime, so the if body is not executed.
    //
    if (!pTmp || LiLtr(NmsHighWaterMarkVersNo, NmsNmhMyMaxVersNo))
    {
         NmsHighWaterMarkVersNo.QuadPart   = LiAdd(NmsVersNoToStartFromNextTime,
                                                NmsHalfRangeSize);
         NmsVersNoToStartFromNextTime.QuadPart = LiAdd(NmsVersNoToStartFromNextTime,
                                                        NmsRangeSize);
    }
    VersNo = NmsVersNoToStartFromNextTime;
    LeaveCriticalSection(&NmsNmhNamRegCrtSec);

    RetVal = RegSetValueEx(
                        sConfigRoot,
                        WINSCNF_INT_VERSNO_NEXTTIME_LW_NM,
                        0,         //reserved -- must be 0
                        REG_DWORD,
                        (LPBYTE)&VersNo.LowPart,
                        sizeof(DWORD)
                                        );


    RetVal2 = RegSetValueEx(
                        sConfigRoot,
                        WINSCNF_INT_VERSNO_NEXTTIME_HW_NM,
                        0,         //reserved -- must be 0
                        REG_DWORD,
                        (LPBYTE)&VersNo.HighPart,
                        sizeof(DWORD)
                                        );
    if ((RetVal != ERROR_SUCCESS) || (RetVal2 != ERROR_SUCCESS))
    {
                DBGPRINT2(ERR, "WinsCnfWriteReg - Could not set Next time's start version counter value in the registry.  The new value is (%d %d)\n", VersNo.HighPart, VersNo.LowPart);
    }

    EnterCriticalSection(&NmsNmhNamRegCrtSec);
    WinsCnfRegUpdThdExists = FALSE;
    LeaveCriticalSection(&NmsNmhNamRegCrtSec);
    DBGLEAVE("WinsCnfWriteReg\n");
    return(WINS_SUCCESS);
}

#if defined (DBGSVC)  && !defined (WINS_INTERACTIVE)
VOID
WinsCnfReadWinsDbgFlagValue(
        VOID
        )
{
        DWORD Sz;
          DWORD ValTyp;

        WinsDbg = 0;   //set it to zero now.  It was set to a value by Init() in
                   //nms.c
        Sz = sizeof(WinsDbg);
        (VOID)RegQueryValueEx(
                             sParametersKey,
                             WINSCNF_DBGFLAGS_NM,
                             NULL,        //reserved; must be NULL
                             &ValTyp,
                             (LPBYTE)&WinsDbg,
                             &Sz
                                );

        return;
}
#endif


VOID
ReadSpecGrpMasks(
        PWINSCNF_CNF_T pWinsCnf
        )

/*++

Routine Description:
        This function is called to read in the special group masks specified
        under the SpecialGrpMasks key

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{
        DWORD NoOfSubKeys;
        HKEY  SGMKey;
        BOOL  fKeyOpen = FALSE;
           LONG  RetVal;
        DBGENTER("ReadSpecGrpMasks\n");
try {
           /*
           *  Open the SPEC_GRP_MASKS key
           */
           RetVal =   RegOpenKeyEx(
                        sParametersKey,                //predefined key value
                        _WINS_CFG_SPEC_GRP_MASKS_KEY,
                        0,                //must be zero (reserved)
                        KEY_READ,        //we desire read access to the keyo
                        &SGMKey                //handle to key
                );
        if (RetVal == ERROR_SUCCESS)
        {
            fKeyOpen = TRUE;
           //
           // Get the count of data files listed under the DATAFILES
           // key
           //
           GetKeyInfo(
                        SGMKey,
                        WINSCNF_E_SPEC_GRP_MASKS_KEY,
                        &NoOfSubKeys,                        //ignored
                        &pWinsCnf->SpecGrpMasks.NoOfSpecGrpMasks
                      );
           if (pWinsCnf->SpecGrpMasks.NoOfSpecGrpMasks > 0)
           {

                DWORD                 Index;
                LPBYTE                 pTmp;
                TCHAR                 ValNmBuff[5];
                DWORD                 ValNmBuffSz;
                DWORD                 StrType;
                LPBYTE                 pByte;
                DWORD                 BuffSize;
                CHAR                 Tmp[WINS_MAX_FILENAME_SZ];
#ifdef UNICODE
                WCHAR                Str[WINSCNF_SPEC_GRP_MASK_SZ];
#endif

                  //
                  // Allocate buffer big enough to hold data for
                // the number of subkeys found under the PULL key
                  //
                  BuffSize = (WINSCNF_SPEC_GRP_MASK_SZ + 1) *
                                pWinsCnf->SpecGrpMasks.NoOfSpecGrpMasks;
                    WinsMscAlloc( BuffSize, &pWinsCnf->SpecGrpMasks.pSpecGrpMasks);

                   /*
                    *   Enumerate  the values
                     */
                     for(
                        Index = 0, pTmp = pWinsCnf->SpecGrpMasks.pSpecGrpMasks;
                        Index <  pWinsCnf->SpecGrpMasks.NoOfSpecGrpMasks;
                                // no third expression
                         )
                {
                        ValNmBuffSz = sizeof(ValNmBuff)/sizeof(TCHAR);  //init before
                                                          //every call
                        BuffSize  = WINSCNF_SPEC_GRP_MASK_SZ;
                          RetVal = RegEnumValue(
                                    SGMKey,
                                    Index,        //key
                                    ValNmBuff,
                                    &ValNmBuffSz,
                                    (LPDWORD)NULL,                //reserved
                                    &StrType,
#ifdef UNICODE
                                    (LPBYTE)Str,
#else
                                    pTmp,
#endif
                                    &BuffSize
                                            );

                        if (RetVal != ERROR_SUCCESS)
                        {
                                continue;
                        }

                        //
                        // if StrType is not REG_SZ  go to the next  Value
                        //
                        if  (StrType != REG_SZ)
                        {
                                continue;
                        }
                        if (BuffSize != WINSCNF_SPEC_GRP_MASK_SZ)
                        {
                                DBGPRINT1(ERR, "ReadSpecGrpMasks: Wrong spec. grp mask (%s)\n", pTmp);

                                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WRONG_SPEC_GRP_MASK_M);
                                continue;
                        }
                        else
                        {
#ifdef UNICODE
                          WinsMscConvertUnicodeStringToAscii(
                                                (LPBYTE)Str,
                                                (LPBYTE)Tmp,
                                                WINSCNF_SPEC_GRP_MASK_SZ
                                                );
#endif
                          pByte = (LPBYTE)Tmp;
                          for (Index = 0; Index < WINSCNF_SPEC_GRP_MASK_SZ;
                                        Index++, pByte++)
                          {
                                *pByte = (BYTE)CharUpperA((LPSTR)*pByte);
                                if (
                                        ((*pByte >= '0') && (*pByte <= '9'))
                                                        ||
                                        ((*pByte >= 'A') && (*pByte <= 'F'))
                                   )
                                {
                                        continue;
                                }
                                else
                                {
                                        break;
                                }

                          }
                          if (Index > WINSCNF_SPEC_GRP_MASK_SZ)
                          {
                                DBGPRINT1(ERR, "ReadSpecGrpMasks: Wrong spec. grp mask (%s)\n", pTmp);
                                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_WRONG_SPEC_GRP_MASK_M);
                                continue;
                          }
                         *(pTmp + WINSCNF_SPEC_GRP_MASK_SZ) = EOS;
                        }

                        Index++;
                        pTmp += WINSCNF_SPEC_GRP_MASK_SZ + 1;
                }

                //
                // If not even one valid name was retrieved, get rid of the
                // buffer
                //
                if (Index == 0)
                {
                        //
                        // Get rid of the buffer
                        //
                        WinsMscDealloc((LPBYTE)pWinsCnf->SpecGrpMasks.pSpecGrpMasks);
                }

                pWinsCnf->SpecGrpMasks.NoOfSpecGrpMasks = Index;
           }
        } // end of if
 } // end of try ..
 except (EXCEPTION_EXECUTE_HANDLER) {
                DBGPRINTEXC("ReadSpecGrpMasks");
                WINSEVT_LOG_D_M(GetExceptionCode(), WINS_EVT_CANT_INIT);
        }

        if (fKeyOpen && RegCloseKey(SGMKey) != ERROR_SUCCESS)
        {
                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_CANT_CLOSE_KEY);
                DBGPRINT0(ERR, "ReadSpecGrpMasks: Can not read the spec. grp. mask. key\n");
        }
        DBGLEAVE("ReadSpecGrpMasks\n");
        return;
}


#if 0
int
__cdecl
CompUpdCnt(
        CONST LPVOID  pElem1,
        CONST LPVOID  pElem2
        )

/*++

Routine Description:
        This function is called by qsort crtl function to compare two
        elements of the array that has to be sorted


Arguments:
        pElem1 - ptr to first element
        pElem1 - ptr to second element

Externals Used:
        None

Return Value:
        -1 if first element is < second element
        = 0 if first element is == second element
        1 if first element is > second element

Error Handling:

Called by:
        qsort (which is called by WinsCnfReadPartnerInfo

Side Effects:

Comments:
        Not used currently
--*/

{

        CONST PRPL_CONFIG_REC_T        pCnfRec1 = pElem1;
        CONST PRPL_CONFIG_REC_T        pCnfRec2 = pElem2;

        if (pCnfRec1->UpdateCount < pCnfRec2->UpdateCount)
        {
                return(-1);
        }
        else
        {
                if (pCnfRec1->UpdateCount == pCnfRec2->UpdateCount)
                {
                        return(0);
                }
        }

        //
        // The first record has a higher UpdateCount than the second one
        //
        return(1);
}

#endif

#ifdef WINSDBG
VOID
PrintRecs(
        RPL_RR_TYPE_E  RRType_e,
        PWINSCNF_CNF_T  pWinsCnf
        )
{
 return;
}
#endif