/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

        winsintf.c

Abstract:
        This module contains the RPC interface to the WINS server

Functions:
        R_WinsRecordAction
        R_WinsStatus
        R_WinsTrigger
        R_WinsDoStaticInit
        R_WinsGetDbRecs
        R_WinsDelDbRecs
        R_WinsSetProcPriority
        WinsRecordAction
        GetWinsStatus
        WinsTrigger
        WinsDoStaticInit
        WinsDoScavenging
        WinsGetDbRecs
        WinsDelDbRecs
        WinsSetProcPriority
        sGetVersNo
        GetConfig
        GetStatistics


Portability:

        This module is portable


Author:

        Pradeep Bahl (PradeepB)          Mar-1993

Revision History:

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

/*
 *       Includes
*/
#include <time.h>
#include "wins.h"
#include <lmerr.h>
#include <lmcons.h>                //defines NET_API_STATUS
#include <secobj.h>
#include <rpcutil.h>                //for NetpRevertToSelf
#include <rpcndr.h>
#include "winsif.h"
#include "winsi2.h"
#include "winsintf.h"
#include "comm.h"
#include "winsque.h"
#include "nms.h"
#include "nmsnmh.h"
#include "nmsmsgf.h"
#include "nmsdb.h"
#include "nmsscv.h"
#include "rpl.h"
#include "rplpull.h"
#include "winscnf.h"
#include "winsevt.h"
#include "winsmsc.h"
#include "winsprs.h"
#include "winstmm.h"
#ifdef WINSDBG
#include "winbasep.h"
#endif

/*
 *        Local Macro Declarations
 */

#if SECURITY > 0
#define  CHK_ACCESS_LEVEL_M(_access)        {                          \
                   if (!sfBS)                                   \
                   {                                            \
                        NET_API_STATUS NetStatus;               \
                        NetStatus = NetpAccessCheckAndAudit(    \
                                        WINS_SERVER,            \
                                        WINS_SERVER,            \
                                        pNmsSecurityDescriptor, \
                                        _access,    \
                                        &NmsInfoMapping         \
                                        );                      \
                        if (NetStatus != NERR_Success)          \
                        {                                       \
                                DBGPRINT1(ERR, "The Caller of the rpc function does not have the required permissions. NetSTatus is (%d)\n", NetStatus);   \
                                WINSEVT_LOG_M(NetStatus, WINS_EVT_NO_PERM);\
                                return(NetStatus);              \
                        }                                       \
                  }                                             \
        }

#else
#define  CHK_ACCESS_LEVEL_M()
#endif

#define INC_RPC_DB_COUNT_NCHK_M    {                         \
                     EnterCriticalSection(&NmsTermCrtSec);   \
                     NmsTotalTrmThdCnt++;                  \
                     LeaveCriticalSection(&NmsTermCrtSec);   \
           }
#define INC_RPC_DB_COUNT_M    {                              \
              if (WinsCnf.State_e != WINSCNF_E_TERMINATING)  \
              {                                              \
                     INC_RPC_DB_COUNT_NCHK_M;                \
              }                                              \
              else                                           \
              {                                              \
                     return(WINSINTF_FAILURE);               \
              }                                              \
           }
#define DEC_RPC_DB_COUNT_M    {                              \
              EnterCriticalSection(&NmsTermCrtSec);          \
              if (--NmsTotalTrmThdCnt == 1)                  \
              {                                              \
                   DBGPRINT0(FLOW, "RPC thread: Signaling the main thread\n");\
                   SetEvent(NmsMainTermEvt);                 \
              }                                              \
              LeaveCriticalSection(&NmsTermCrtSec);          \
           }
/*
 *        Local Typedef Declarations
 */



/*
 *        Global Variable Definitions
 */
WINSINTF_STAT_T        WinsIntfStat = {0};

DWORD                WinsIntfNoOfNbtThds;
DWORD                WinsIntfNoCncrntStaticInits = 0;
//DWORD                WinsIntfNoOfRpcThds = 0;
CRITICAL_SECTION WinsIntfCrtSec;

CRITICAL_SECTION WinsIntfPotentiallyLongCrtSec;
CRITICAL_SECTION WinsIntfNoOfUsersCrtSec;  //extern in nms.h

/*
 *        Local Variable Definitions
*/
STATIC  BOOL    sfBS = FALSE;

//
// Time interval between reflushing of the 1B cache
//
#define THREE_MTS 180

//
// Used by WinsGetBrowserNames
//
DOM_CACHE_T sDomCache = { 0, NULL, 0, 0, NULL, FALSE};

/*
 *        Local Function Prototype Declarations
 */
DWORD
GetWinsStatus(
   IN  WINSINTF_CMD_E          Cmd_e,
   OUT LPVOID  pResults,
   BOOL fNew
        );

STATIC
DWORD
sGetVersNo(
        LPVOID  pResults
        );

STATIC
DWORD
GetStatistics(
        LPVOID  pResults,
        BOOL  fNew
        );

STATIC
DWORD
GetConfig(
        LPVOID   pResults,
        BOOL     fNew,
        BOOL     fAllMaps
        );

VOID
LogClientInfo(
        RPC_BINDING_HANDLE ClientHdl,
        BOOL               fAbruptTerm
  );


STATIC
STATUS
PackageRecs(
        PRPL_REC_ENTRY2_T     pBuff,
        DWORD                BuffLen,
        DWORD                NoOfRecs,
        PWINSINTF_RECS_T     pRecs
     );


//
// Function definitions start here
//

DWORD
R_WinsCheckAccess(
    WINSIF2_HANDLE        ServerHdl,
    DWORD                 *Access
    )
{
    NET_API_STATUS NetStatus;
    *Access = WINS_NO_ACCESS;
    NetStatus = NetpAccessCheckAndAudit(
                    WINS_SERVER,
                    WINS_SERVER,
                    pNmsSecurityDescriptor,
                    WINS_CONTROL_ACCESS,
                    &NmsInfoMapping
                    );
    if (NERR_Success == NetStatus) {
        *Access = WINS_CONTROL_ACCESS;
        return WINSINTF_SUCCESS;
    }
    NetStatus = NetpAccessCheckAndAudit(
                    WINS_SERVER,
                    WINS_SERVER,
                    pNmsSecurityDescriptor,
                    WINS_QUERY_ACCESS,
                    &NmsInfoMapping
                    );
    if (NERR_Success == NetStatus) {
        *Access = WINS_QUERY_ACCESS;
        return WINSINTF_SUCCESS;
    }
    return WINSINTF_SUCCESS;
}

DWORD
R_WinsRecordAction(
        PWINSINTF_RECORD_ACTION_T        *ppRecAction
        )

/*++

Routine Description:
        This function is called to insert/update/delete a record

Arguments:
        pRecAction - Record Information

Externals Used:
        None

Return Value:

   Success status codes --
   Error status codes  --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{

  DWORD                Status = WINSINTF_FAILURE;

PERF("Use & and || logic")

  if (*ppRecAction == NULL)
  {
       return(Status);
  }
  if ((WinsCnf.State_e == WINSCNF_E_RUNNING) || (WinsCnf.State_e == WINSCNF_E_PAUSED))
  {
          if (WINSINTF_E_QUERY == (*ppRecAction)->Cmd_e) {
              CHK_ACCESS_LEVEL_M(WINS_QUERY_ACCESS);
          } else {
              CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
          }
          INC_RPC_DB_COUNT_NCHK_M;
try {
          Status = WinsRecordAction(ppRecAction);
}
finally {
          DEC_RPC_DB_COUNT_M;
}
  }

  return(Status);
}


DWORD
R_WinsStatusWHdl (
   WINSIF_HANDLE             pWinsHdl,
   WINSINTF_CMD_E          Cmd_e,
   PWINSINTF_RESULTS_NEW_T  pResults
        )
{
    return(R_WinsStatusNew(Cmd_e, pResults));

}

DWORD
R_WinsStatus (
  // LPTSTR                pWinsAddStr,
   WINSINTF_CMD_E          Cmd_e,
   PWINSINTF_RESULTS_T  pResults
        )
{

  DWORD                Status = WINSINTF_FAILURE;

  //
  // Make sure that the WINS is in steady state
  //
PERF("Use & and || logic")
  if ((WinsCnf.State_e == WINSCNF_E_RUNNING) || (WinsCnf.State_e == WINSCNF_E_PAUSED))
  {
     CHK_ACCESS_LEVEL_M(WINS_QUERY_ACCESS);
     Status = GetWinsStatus(/*pWinsAddStr,*/Cmd_e, pResults, FALSE);
  }
  return(Status);
}
DWORD
R_WinsStatusNew (
   WINSINTF_CMD_E          Cmd_e,
   PWINSINTF_RESULTS_NEW_T     pResults
        )
{

  DWORD                Status = WINSINTF_FAILURE;

  //
  // Make sure that the WINS is in steady state
  //
PERF("Use & and || logic")
  if ((WinsCnf.State_e == WINSCNF_E_RUNNING) || (WinsCnf.State_e == WINSCNF_E_PAUSED))
  {
     CHK_ACCESS_LEVEL_M(WINS_QUERY_ACCESS);
     Status = GetWinsStatus(Cmd_e, pResults, TRUE);
  }
  return(Status);
}

DWORD
R_WinsTrigger (
        PWINSINTF_ADD_T             pWinsAdd,
        WINSINTF_TRIG_TYPE_E          TrigType_e
        )
{
  DWORD                Status = WINSINTF_FAILURE;
PERF("Use & and || logic")
  if ((WinsCnf.State_e == WINSCNF_E_RUNNING) || (WinsCnf.State_e == WINSCNF_E_PAUSED))
  {
        CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
          Status = WinsTrigger(pWinsAdd, TrigType_e);
  }
  return(Status);
}

DWORD
R_WinsDoStaticInit (
        LPWSTR         pDataFilePath,
        DWORD          fDel
        )
{
  DWORD                Status;

  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  EnterCriticalSection(&WinsIntfCrtSec);

  //
  // The admin tool can go haywire and create a lot of threads.  Limit it
  // to a certain max value. The value will be incremented and decremented
  // by the thread doing the STATIC initialization.
  //
  if (WinsIntfNoCncrntStaticInits > WINSCNF_MAX_CNCRNT_STATIC_INITS)
  {
          LeaveCriticalSection(&WinsIntfCrtSec);
    DBGPRINT1(ERR, "R_WinsDoStaticInit: Too many concurrent STATIC inits. No is (%d)\n", WinsIntfNoCncrntStaticInits);
    WINSEVT_LOG_D_M(WinsIntfNoCncrntStaticInits, WINS_EVT_TOO_MANY_STATIC_INITS);
        return(WINSINTF_TOO_MANY_STATIC_INITS);
  }
  LeaveCriticalSection(&WinsIntfCrtSec);
  Status = WinsDoStaticInit(pDataFilePath, fDel);
  return(Status);
}

DWORD
R_WinsDoScavenging (
        VOID
        )
{
  DWORD                Status;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
  Status = WinsDoScavenging();
}
finally {
  DEC_RPC_DB_COUNT_M;
}
  return(Status);
}

DWORD
R_WinsDoScavengingNew (
        PWINSINTF_SCV_REQ_T  pScvReq
        )
{
  DWORD                Status;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
  Status = WinsDoScavengingNew(pScvReq);
}
finally {
  DEC_RPC_DB_COUNT_M;
}
  return(Status);
}

DWORD
R_WinsGetDbRecs (
        PWINSINTF_ADD_T             pWinsAdd,
        WINSINTF_VERS_NO_T          MinVersNo,
        WINSINTF_VERS_NO_T          MaxVersNo,
        PWINSINTF_RECS_T          pRecs
        )
{
  DWORD                Status;
  CHK_ACCESS_LEVEL_M(WINS_QUERY_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
#if 0
#ifdef WINSDBG
   PVOID pCallersAdd, pCallersCaller;
   RtlGetCallersAddress(&pCallersAdd, &pCallersCaller);
   DbgPrint("Callers Address = (%x)\nCallersCaller = (%x)\n", pCallersAdd, pCallersCaller);

#endif
#endif
  Status = WinsGetDbRecs(pWinsAdd, MinVersNo, MaxVersNo, pRecs);
}
finally {
  DEC_RPC_DB_COUNT_M;
}
  return(Status);
}
DWORD
R_WinsGetDbRecsByName (
        PWINSINTF_ADD_T             pWinsAdd,
        DWORD                       Location,
        LPBYTE                      pName,
        DWORD                       NameLen,
        DWORD                       NoOfRecsDesired,
        DWORD                       fStaticOnly,
        PWINSINTF_RECS_T            pRecs
        )
{
  DWORD                Status;
  CHK_ACCESS_LEVEL_M(WINS_QUERY_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
#if 0
#ifdef WINSDBG
   PVOID pCallersAdd, pCallersCaller;
   RtlGetCallersAddress(&pCallersAdd, &pCallersCaller);
#endif
#endif
  Status = WinsGetDbRecsByName(pWinsAdd, Location, pName, NameLen, NoOfRecsDesired,
                   fStaticOnly, pRecs);
}
finally {
  DEC_RPC_DB_COUNT_M;
}
  return(Status);
}



DWORD
R_WinsDeleteWins(
        PWINSINTF_ADD_T   pWinsAdd
        )
{
  DWORD                Status;
  //LogClientInfo();
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
  Status = WinsDeleteWins(pWinsAdd);
}
finally {
  DEC_RPC_DB_COUNT_M;
}
  return(Status);
}

DWORD
R_WinsTerm (
        handle_t                ClientHdl,
        short                        fAbruptTerm
        )
{
  DWORD                Status;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  Status = WinsTerm(ClientHdl, fAbruptTerm);
  LogClientInfo(ClientHdl, fAbruptTerm);
  return(Status);
}


DWORD
R_WinsBackup (
   LPBYTE                pBackupPath,
   short                fIncremental
        )
{

  DWORD                Status = WINSINTF_FAILURE;
  BYTE                BackupPath[WINS_MAX_FILENAME_SZ + sizeof(WINS_BACKUP_DIR_ASCII)];
#if 0
  (VOID)WinsMscConvertUnicodeStringToAscii(pBackupPath, BackupPath, WINS_MAX_FILENAME_SZ);
#endif
FUTURES("expensive.  Change idl prototype to pass length")
   if (strlen(pBackupPath) > WINS_MAX_FILENAME_SZ)
   {
         return(Status);
   }
  //
  // Make sure that the WINS is in steady state
  //
PERF("Use & and || logic")
  if ((WinsCnf.State_e == WINSCNF_E_RUNNING) || (WinsCnf.State_e == WINSCNF_E_PAUSED))
  {
      CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
      INC_RPC_DB_COUNT_NCHK_M;
      WinsLogAdminEvent( WINS_EVT_ADMIN_BACKUP_INITIATED, 0 );
try {
      strcpy(BackupPath, pBackupPath);
      strcat(BackupPath, WINS_BACKUP_DIR_ASCII);
      if (CreateDirectoryA(BackupPath, NULL) || ((Status = GetLastError()) ==
                                                    ERROR_ALREADY_EXISTS))
      {
         Status = WinsBackup(BackupPath, fIncremental);
      }
}
finally {
      DEC_RPC_DB_COUNT_M;
}
  }
  return(Status);
}

DWORD
R_WinsDelDbRecs(
        IN PWINSINTF_ADD_T        pAdd,
        IN WINSINTF_VERS_NO_T        MinVersNo,
        IN WINSINTF_VERS_NO_T        MaxVersNo
        )
{

  DWORD                Status = WINSINTF_FAILURE;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
  Status = WinsDelDbRecs(pAdd, MinVersNo, MaxVersNo);
}
finally {
   DEC_RPC_DB_COUNT_M;
   }
  return(Status);
}

DWORD
R_WinsTombstoneDbRecs(
        IN WINSIF2_HANDLE            ServerHdl,
        IN PWINSINTF_ADD_T           pWinsAdd,
        IN WINSINTF_VERS_NO_T        MinVersNo,
        IN WINSINTF_VERS_NO_T        MaxVersNo
        )
{

  DWORD                Status = WINSINTF_FAILURE;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  INC_RPC_DB_COUNT_M;
try {
  Status = WinsTombstoneDbRecs(pWinsAdd, MinVersNo, MaxVersNo);
}
finally {
   DEC_RPC_DB_COUNT_M;
   }
  return(Status);
}

DWORD
R_WinsPullRange(
        IN PWINSINTF_ADD_T        pAdd,
        IN PWINSINTF_ADD_T        pOwnerAdd,
        IN WINSINTF_VERS_NO_T        MinVersNo,
        IN WINSINTF_VERS_NO_T        MaxVersNo
        )
{

  DWORD                Status = WINSINTF_FAILURE;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  Status = WinsPullRange(pAdd, pOwnerAdd, MinVersNo, MaxVersNo);
  return(Status);
}

DWORD
R_WinsSetPriorityClass(
        IN WINSINTF_PRIORITY_CLASS_E        PriorityClass
        )
{

  DWORD                Status = WINSINTF_FAILURE;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  Status = WinsSetPriorityClass(PriorityClass);
  return(Status);
}

DWORD
R_WinsResetCounters(
        VOID
        )
{

  DWORD                Status = WINSINTF_FAILURE;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  Status = WinsResetCounters();
  return(Status);
}

DWORD
R_WinsWorkerThdUpd(
        DWORD NewNoOfNbtThds
        )
{
  DWORD                Status = WINSINTF_FAILURE;
  CHK_ACCESS_LEVEL_M(WINS_CONTROL_ACCESS);
  Status = WinsWorkerThdUpd(NewNoOfNbtThds);
  return(Status);
}

DWORD
R_WinsGetNameAndAdd(
        PWINSINTF_ADD_T   pWinsAdd,
        LPBYTE                  pUncName
        )
{
  DWORD                Status = WINSINTF_FAILURE;
  //CHK_ACCESS_LEVEL_M();
  Status = WinsGetNameAndAdd(pWinsAdd, pUncName);
  return(Status);
}

DWORD
R_WinsGetBrowserNames_Old(
        PWINSINTF_BROWSER_NAMES_T         pNames
        )
{
  return(WINSINTF_FAILURE);
}



DWORD
R_WinsGetBrowserNames(
        WINSIF_HANDLE             pWinsHdl,
        PWINSINTF_BROWSER_NAMES_T         pNames
        )
{
  DWORD                Status = WINSINTF_FAILURE;

  static DWORD    sNoOfReq = 0;

  //
  // Allow access to anybody.  We don't check access here since
  // browser running as a service has zero access when it goes
  // on the network (It goes under the null account -- CliffVDyke 4/15/94)
  //
  INC_RPC_DB_COUNT_M;
  EnterCriticalSection(&WinsIntfPotentiallyLongCrtSec);
try {
  if (sNoOfReq++ < NMS_MAX_BROWSER_RPC_CALLS)
  {
    Status = WinsGetBrowserNames((PWINSINTF_BIND_DATA_T)pWinsHdl,pNames);
  }
  else
  {
        pNames->EntriesRead = 0;
        pNames->pInfo = NULL;
        Status = WINSINTF_FAILURE;
  }
 } // end of try
finally {
  sNoOfReq--;

  //
  //  increment the user count.
  //
  EnterCriticalSection(&WinsIntfNoOfUsersCrtSec);
  sDomCache.NoOfUsers++;
  LeaveCriticalSection(&WinsIntfNoOfUsersCrtSec);

  LeaveCriticalSection(&WinsIntfPotentiallyLongCrtSec);
  DEC_RPC_DB_COUNT_M;

 }
  return(Status);
}


DWORD
R_WinsSetFlags (
        DWORD    fFlags
        )
{
  DWORD                Status = WINSINTF_SUCCESS;

  Status = WinsSetFlags(fFlags);
  return(Status);
}

DWORD
WinsSetFlags (
        DWORD    fFlags
        )
{
  DWORD                Status = WINSINTF_SUCCESS;
#ifdef WINSDBG
  DWORD                DbgFlagsStore = WinsDbg;
  SYSTEMTIME           SystemTime;
  BOOL                 sHaveProcessHeapHdl = FALSE;
  HANDLE               PrHeapHdl;

  typedef struct _HEAP_INFO_T {
     HANDLE HeapHdl;
     LPBYTE cstrHeapType;
     } HEAP_INFO_T, *PHEAP_INFO_T;

#define PRINT_TIME_OF_DUMP_M(SystemTime, Str)  {DBGPRINT5(SPEC, "Activity: %s done on %d/%d at %d.%d \n", Str, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute); }

#endif

  //sfBS = fFlags & WINSINTF_BS;

  DBGPRINT2(ERR, "WinsSetFlags: NmsDbDelDelDataRecs = (%d)\nNmsDbDelQueryNUpdRecs = (%d)\n", NmsDbDelDelDataRecs, NmsDbDelQueryNUpdRecs);

#ifdef WINSDBG
  if (!sHaveProcessHeapHdl)
  {
    PrHeapHdl = GetProcessHeap();
  }
  GetSystemTime(&SystemTime);
  WinsDbg |= DBG_SPEC;
  if (fFlags & WINSINTF_MEMORY_INFO_DUMP)
  {
     MEMORYSTATUS Mem;
     static SIZE_T  sLTVmUsed = 0;
     SIZE_T VmUsed;

     DBGPRINT0(SPEC, "\n\n------------------MEMORY USAGE INFO-----------------\n\n");
     GlobalMemoryStatus(&Mem);

     VmUsed = Mem.dwTotalVirtual - Mem.dwAvailVirtual;
     DBGPRINT2(SPEC, "VM used = (%d)\nDiff. from last time = (%d)\n", VmUsed,
         VmUsed - sLTVmUsed);
     sLTVmUsed = VmUsed;

  }
  EnterCriticalSection(&WinsCnfCnfCrtSec);
try {
  if (fFlags & WINSINTF_HEAP_INFO_DUMP)
  {
      HEAP_INFO_T HeapHdls[] = {
             CommUdpBuffHeapHdl,    "Udp Buff Heap",
             CommUdpDlgHeapHdl,      "Udp Dlg Heap",
             CommAssocDlgHeapHdl,    "Tcp Dlg Heap",
             CommAssocTcpMsgHeapHdl, "Tcp Msg Heap",
             GenBuffHeapHdl,         "General Heap",
             QueBuffHeapHdl,         "Que Wrk. Item Heap",
             NmsChlHeapHdl,           "Chl Req/Resp Heap",
             CommAssocAssocHeapHdl,   "Assoc. Msg Heap",
             RplWrkItmHeapHdl,        "Rpl Wrk Itm Heap",
             NmsRpcHeapHdl,           "Rpc Buff Heap",
             WinsTmmHeapHdl,          "Tmm Buff Heap",
             (LPHANDLE)NULL,                    NULL
                            };
     static SIZE_T sDiffLTHeapTotalANF[sizeof(HeapHdls)/sizeof(HEAP_INFO_T)] = {0};
     static SIZE_T sHeapTotalANF[sizeof(HeapHdls)/sizeof(HEAP_INFO_T)] = {0};
     SIZE_T  Size2;
     static SIZE_T  sTotalAllocNFree = 0;
     static SIZE_T  LastTimeTotalAllocNFree = 0;
//     NTSTATUS  Status;
     HANDLE  HeapHdl;
     DWORD  i, n;
     DWORD dwNumberOfHeaps;
     PHANDLE pPrHeaps;
     HEAP_SUMMARY heapSummary;

     PRINT_TIME_OF_DUMP_M(SystemTime, "HEAP DUMP");
     dwNumberOfHeaps = GetProcessHeaps(0, NULL);
     Size2 = sizeof(*pPrHeaps) * dwNumberOfHeaps;
     pPrHeaps = WinsMscHeapAlloc( NmsRpcHeapHdl, (ULONG)Size2);
     dwNumberOfHeaps = GetProcessHeaps(dwNumberOfHeaps, pPrHeaps);

     DBGPRINT1(SPEC, "No Of Heaps is (%d)\n",  dwNumberOfHeaps);
     DBGPRINT1(SPEC, "Process default heap handle is (%p)\n",  PrHeapHdl);
     LastTimeTotalAllocNFree = sTotalAllocNFree;
     sTotalAllocNFree = 0;
     heapSummary.cb = sizeof(HEAP_SUMMARY);
     for (i=0; i< dwNumberOfHeaps; i++)
     {
        DBGPRINT0(SPEC, "----------Heap Info--------------------------\n");
        DBGPRINT0(SPEC, "Heap -- ");

        HeapHdl = pPrHeaps[i];
        for (n = 0;  HeapHdls[n].HeapHdl != NULL; n++)
        {
            if (HeapHdl == HeapHdls[n].HeapHdl)
            {
              DBGPRINT1(SPEC, "%s\n", HeapHdls[n].cstrHeapType);
              break;
            }
        }
        if (HeapHdls[n].HeapHdl == NULL)
        {

            DBGPRINT0(SPEC, "Catch all Heap\n");
        }

        DBGPRINT1(SPEC, "Heap Hdl = (%p)\n", HeapHdl);
        if (HeapSummary(HeapHdl, 0, &heapSummary))
        {
           DBGPRINT2(SPEC,"Total Allocated = (%d)\nTotalFree = (%d)\n",
                        heapSummary.cbAllocated, heapSummary.cbCommitted - heapSummary.cbAllocated);
        }
        else
        {
           DBGPRINT0(SPEC,"COULD NOT GET HEAP INFO\n");
           continue;
        }



        sDiffLTHeapTotalANF[n] = heapSummary.cbCommitted - sHeapTotalANF[n];
        sHeapTotalANF[n] = heapSummary.cbCommitted;
        sTotalAllocNFree += sHeapTotalANF[n];
        DBGPRINT1(SPEC, "Size allocated from RpcHeap is  (%d)\n", Size2);
     } // end of for looping over process heaps

     DBGPRINT0(SPEC, "\n----------Heap Info End --------------------------\n");
     WinsMscHeapFree(NmsRpcHeapHdl, pPrHeaps);

     for (n = 0;  HeapHdls[n].HeapHdl != NULL; n++)
     {
              DBGPRINT3(SPEC, "%s -- Total AllocNFree = (%d); Diff from Last time = (%d)\n",
                      HeapHdls[n].cstrHeapType, sHeapTotalANF[n],
                      sDiffLTHeapTotalANF[n]
                      );
     }
     DBGPRINT2(SPEC, "\nTotal Process AllocNFree = (%d)\nDiff from last time = (%d)\n\n",  sTotalAllocNFree, sTotalAllocNFree - LastTimeTotalAllocNFree);

     if (WinsDbg & (DBG_HEAP_CNTRS | DBG_UPD_CNTRS))
     {
        NmsPrintCtrs();
     }
    }
    if (fFlags & WINSINTF_QUE_ITEMS_DUMP)
    {
       typedef struct _QUE_INFO_T {
          PQUE_HD_T  pQueHd;
          LPBYTE     cstrQueType;
         } QUE_INFO_T, *PQUE_INFO_T;

       QUE_INFO_T  Queues[] = {
            &QueNbtWrkQueHd,     "Nbt Query Que",
	    &QueOtherNbtWrkQueHd, "Nbt Reg. Que",
  	    &QueRplPullQueHd,   "Pull Thd Que",          //Pull requests
	    &QueRplPushQueHd,   "Push Thd Que",          //Push requests
	    &QueNmsNrcqQueHd,   "Chl Nbt Req. Msg Que",  //Chl req fr nbt thds
	    &QueNmsRrcqQueHd,   "Chl req. from Pull thd Que",
	    &QueNmsCrqQueHd,    "Chl rsp from UDP thd Que",
	    &QueWinsTmmQueHd,   "Timer Queue",
	    &QueInvalidQueHd,   "Invalid Que"
                        };
        PQUE_INFO_T pQueInfo = Queues;
        PRINT_TIME_OF_DUMP_M(SystemTime, "WORK ITEM DUMP");

        DBGPRINT0(SPEC, "----------Count of Wrk items-----------\n");
        for (; pQueInfo->pQueHd != &QueInvalidQueHd; pQueInfo++)
        {
             PLIST_ENTRY pTmp;
             DWORD NoOfWrkItms = 0;
             pTmp = &pQueInfo->pQueHd->Head;
             EnterCriticalSection(&pQueInfo->pQueHd->CrtSec);
//             NoOfWrkItms = pQueInfo->pQueHd->NoOfEntries;
//#if 0

             while (pTmp->Flink != &pQueInfo->pQueHd->Head)
             {

                     NoOfWrkItms++;
                     pTmp = pTmp->Flink;
             }
//#endif
             LeaveCriticalSection(&pQueInfo->pQueHd->CrtSec);
             DBGPRINT2(SPEC, "Que = (%s) has (%d) wrk items\n",
                                   pQueInfo->cstrQueType,
                                   NoOfWrkItms
                                     );
        }

        DBGPRINT0(SPEC, "----------Count of Wrk items End-----------\n");
     }
} // end of try
finally {
      if (AbnormalTermination())
      {
         DBGPRINT0(SPEC, "WinsSetFlags terminated abnormally\n");
      }
      WinsDbg = DbgFlagsStore;
      LeaveCriticalSection(&WinsCnfCnfCrtSec);
 }  //end of finally { }
#endif
  return(Status);
}

DWORD
WinsBackup (
   LPBYTE                pBackupPath,
   short                 fIncremental
        )
{
 DWORD RetVal = WINS_FAILURE;
 try {
#if 0
   RetVal = NmsDbBackup(pBackupPath, fIncremental ? NMSDB_INCREMENTAL_BACKUP :
                        NMSDB_FULL_BACKUP);
#endif
   //
   // Always do full backup until Jet is solid enough in doing incremental
   // backups. Ian does not seem very sure about how robust it is currently.
   // (7/6/94)
   //
   RetVal = NmsDbBackup(pBackupPath, NMSDB_FULL_BACKUP);
  }
 except(EXCEPTION_EXECUTE_HANDLER) {
    DBGPRINTEXC("WinsBackup");
#if 0
    DBGPRINT2(ERR, "WinsBackup: Could not do %s backup to dir (%s)\n", fIncremental ? "INCREMENTAL" : "FULL", pBackupPath);

    DBGPRINT1(ERR, "WinsBackup: Could not do full backup to dir (%s)\n", pBackupPath);
#endif
    WinsEvtLogDetEvt(FALSE, WINS_EVT_BACKUP_ERR, NULL, __LINE__, "s", pBackupPath);
 }
 if (RetVal != WINS_SUCCESS)
 {
#if 0
   RetVal = fIncremental ? WINSINTF_INC_BACKUP_FAILED : WINSINTF_FULL_BACKUP_FAILED;
#endif
   RetVal = WINSINTF_FULL_BACKUP_FAILED;
 }
 else
 {
   RetVal = WINSINTF_SUCCESS;
 }
 return(RetVal);
}


DWORD
WinsTerm (
        handle_t        ClientHdl,
        short                fAbruptTerm
        )
{
  DBGPRINT1(FLOW, "WINS TERMINATED %s BY ADMINISTRATOR\n", fAbruptTerm ? "ABRUPTLY" : "GRACEFULLY");

  UNREFERENCED_PARAMETER(ClientHdl);

  if (fAbruptTerm)
  {
        fNmsAbruptTerm = TRUE;
          WinsMscSignalHdl(NmsMainTermEvt);
          ExitProcess(WINS_SUCCESS);

//        EnterCriticalSection(&NmsTermCrtSec);
//        NmsTotalTrmThdCnt = 0;  //force the count to less than 0
//        LeaveCriticalSection(&NmsTermCrtSec);
  }

  WinsMscSignalHdl(NmsMainTermEvt);
  return(WINSINTF_SUCCESS);
}

DWORD
WinsRecordAction(
        PWINSINTF_RECORD_ACTION_T        *ppRecAction
        )

/*++

Routine Description:
        This function is called to register, query, release a name

Arguments:
        pRecAction - Info about the operation to do and the name to insert,
                     query or release

Externals Used:
        None


Return Value:

   Success status codes -- WINSINTF_SUCCESS
   Error status codes   -- WINSINTF_FAILURE

Error Handling:

Called by:
        R_WinsRecordAction()

Side Effects:

Comments:
        None
--*/

{

  STATUS             RetStat;
  RPL_REC_ENTRY_T   Rec;
  NMSDB_STAT_INFO_T StatInfo;
  NMSMSGF_CNT_ADD_T CntAdd;
  DWORD                    i, n;
  BOOL                    fSwapped = FALSE;
  PWINSINTF_RECORD_ACTION_T pRecAction = *ppRecAction;

  NmsDbThdInit(WINS_E_WINSRPC);
  NmsDbOpenTables(WINS_E_WINSRPC);
  DBGMYNAME("RPC-WinsRecordAction");

try {
  CntAdd.NoOfAdds          = 1;
  CntAdd.Add[0].AddTyp_e  = pRecAction->Add.Type;
  CntAdd.Add[0].AddLen    = pRecAction->Add.Len;
  CntAdd.Add[0].Add.IPAdd = pRecAction->Add.IPAdd;




  //
  // Check to see if it is a PDC name (0x1B in the 16th byte).  Do this only
  // if the name is atleast 16 bytes long.  Winscl or some other tool may
  // send a shorter name. Netbt will never send a shorter name.
  //
  if ((pRecAction->NameLen >= (WINS_MAX_NS_NETBIOS_NAME_LEN - 1)) && (*(pRecAction->pName + 15) == 0x1B))
  {
        WINS_SWAP_BYTES_M(pRecAction->pName, pRecAction->pName + 15);
        fSwapped = TRUE;
  }

  //
  // just in case the admin. tool is screwing up and passing us an invalid
  // name length, adjust the length.
  //
  if (pRecAction->NameLen > WINS_MAX_NAME_SZ)
  {
      pRecAction->NameLen = WINS_MAX_NAME_SZ - 1;
  }

  //
  // Terminate name with NULL, just in case user didn't do it.
  //
  *(pRecAction->pName + pRecAction->NameLen) = (TCHAR)NULL;

  switch(pRecAction->Cmd_e)
  {
        case(WINSINTF_E_INSERT):

                if (pRecAction->TypOfRec_e == WINSINTF_E_UNIQUE)
                {

                 RetStat = NmsNmhNamRegInd(
                                NULL,                   //no dialogue handle
                                (LPBYTE)pRecAction->pName,
                                pRecAction->NameLen + 1, //to include the ending                                                         //0 byte. See GetName()
                                                         //in nmsmsgf.c
                                CntAdd.Add,
                                pRecAction->NodeTyp,
                                NULL,
                                0,
                                0,
                                FALSE,        //it is a name reg (nor a ref)
                                pRecAction->fStatic,
                                TRUE                  // administrative action
                                );
                }
                else  // the record is a group or  multihomed
                {

                if (
                        (pRecAction->TypOfRec_e == WINSINTF_E_MULTIHOMED )
                                        ||
                        (pRecAction->TypOfRec_e == WINSINTF_E_SPEC_GROUP )
                   )
                {
                        for (i = 0; i < pRecAction->NoOfAdds; i++)
                        {
                             //
                             // pAdd is a unique pointer and so can be 
                             // NULL.  We however do not protect oureelves
                             // here for the following reasons
                             //
                             //  - the call can only be executed by Admins
                             //    on this machine and through tools
                             //    that MS provides that do not pass pAdd as
                             //    NULL.  The rpc call is not published
                             //
                             //  - AV will be caught and a failure returned.
                             //    No harm done.
                             //
                             CntAdd.Add[i].AddTyp_e  =
                                        (pRecAction->pAdd + i)->Type;
                             CntAdd.Add[i].AddLen    =
                                        (pRecAction->pAdd + i)->Len;
                             CntAdd.Add[i].Add.IPAdd =
                                        (pRecAction->pAdd + i)->IPAdd;
                        }
                        CntAdd.NoOfAdds          = pRecAction->NoOfAdds;

                }
                RetStat= NmsNmhNamRegGrp(
                                NULL,                   //no dialogue handle
                                (LPBYTE)pRecAction->pName,
                                pRecAction->NameLen + 1,
                                &CntAdd,
                                0,                //node type (not used)
                                NULL,
                                0,
                                0,
                                pRecAction->TypOfRec_e == WINSINTF_E_MULTIHOMED ? NMSDB_MULTIHOMED_ENTRY : (NMSDB_IS_IT_SPEC_GRP_NM_M(pRecAction->pName) || (pRecAction->TypOfRec_e == WINSINTF_E_NORM_GROUP) ? NMSDB_NORM_GRP_ENTRY : NMSDB_SPEC_GRP_ENTRY),
                                FALSE,        //it is a name reg (nor a ref)
                                pRecAction->fStatic,
                                TRUE                  // administrative action
                                );
                }
                break;

        case(WINSINTF_E_RELEASE):

                if (
                        (pRecAction->TypOfRec_e == WINSINTF_E_MULTIHOMED)
                                            ||
                        (pRecAction->TypOfRec_e == WINSINTF_E_SPEC_GROUP)
                   )
                {
                        if (pRecAction->pAdd != NULL)
                        {
                             CntAdd.Add[0].AddTyp_e  =  pRecAction->pAdd->Type;
                             CntAdd.Add[0].AddLen    =  pRecAction->pAdd->Len;
                             CntAdd.Add[0].Add.IPAdd =  pRecAction->pAdd->IPAdd;
                        }
                }


                RetStat = NmsNmhNamRel(
                                NULL,                   //no dialogue handle
                                (LPBYTE)pRecAction->pName,
                                pRecAction->NameLen + 1,
                                CntAdd.Add,
                                pRecAction->TypOfRec_e ==
                                        WINSINTF_E_UNIQUE ? FALSE : TRUE,
                                NULL,
                                0,
                                0,
                                TRUE                  // administrative action
                                     );

                break;

        case(WINSINTF_E_QUERY):

                //
                // Anybody can query a record.  We don't have any security 
                // for plain queries. Therefore somebody can cause a leak
                // by making calls with pAdd pointing to allocated memory.
                // Let us free that memory and proceed.  We can return 
                // failure also but let us cover for our legit caller's 
                // mistakes.  Currently, the only known callers of this
                // function are winsmon, winscl, winsadmn (NT 4 and before)
                // and wins snapin.   
                //
                if (pRecAction->pAdd != NULL)
                {
                     midl_user_free(pRecAction->pAdd);
                }
                RetStat = NmsNmhNamQuery(
                            NULL,                   //no dialogue handle
                            (LPBYTE)pRecAction->pName,
                            pRecAction->NameLen + 1,
                            NULL,
                            0,
                            0,
                            TRUE,                  // administrative action
                            &StatInfo
                             );

                if (RetStat == WINS_SUCCESS)
                {
                  pRecAction->TypOfRec_e = StatInfo.EntTyp;
                  pRecAction->OwnerId    = StatInfo.OwnerId;
                  pRecAction->State_e    =
                                        (WINSINTF_STATE_E)StatInfo.EntryState_e;
                  pRecAction->TimeStamp    = StatInfo.TimeStamp;
                  if (
                        NMSDB_ENTRY_UNIQUE_M(StatInfo.EntTyp)
                                ||
                        NMSDB_ENTRY_NORM_GRP_M(StatInfo.EntTyp)
                   )
                 {
                    pRecAction->NoOfAdds    = 0;
                    pRecAction->pAdd       = NULL;
                    pRecAction->Add.IPAdd  =
                                StatInfo.NodeAdds.Mem[0].Add.Add.IPAdd;
                    pRecAction->Add.Type  =
                                (UCHAR) StatInfo.NodeAdds.Mem[0].Add.AddTyp_e;
                    pRecAction->Add.Len  =
                                StatInfo.NodeAdds.Mem[0].Add.AddLen;
                 }
                 else
                 {

                  PNMSDB_WINS_STATE_E pWinsState_e;
                  PCOMM_ADD_T         pAdd;
                  PVERS_NO_T     pStartVersNo;

                  EnterCriticalSection(&NmsDbOwnAddTblCrtSec);
try {
                  if (StatInfo.NodeAdds.NoOfMems > 0)
                  {
                    pRecAction->NoOfAdds = StatInfo.NodeAdds.NoOfMems * 2;
                    pRecAction->pAdd = midl_user_allocate(pRecAction->NoOfAdds * sizeof(WINSINTF_ADD_T));
                  }
                  else
                  {
                    pRecAction->NoOfAdds = 0;
                    pRecAction->pAdd = NULL;
                  }
                  for (
                        n = 0, i = 0;
                        n < (StatInfo.NodeAdds.NoOfMems)  && n < WINSINTF_MAX_MEM;                         n++
                     )
                  {
                        RPL_FIND_ADD_BY_OWNER_ID_M(
                                  StatInfo.NodeAdds.Mem[n].OwnerId,
                                  pAdd,
                                  pWinsState_e,
                                  pStartVersNo
                                        );
                       (pRecAction->pAdd + i++)->IPAdd = pAdd->Add.IPAdd;

                       (pRecAction->pAdd + i++)->IPAdd   =
                          StatInfo.NodeAdds.Mem[n].Add.Add.IPAdd;


                  }
}
except(EXCEPTION_EXECUTE_HANDLER) {
        DWORD ExcCode = GetExceptionCode();
        WINSEVT_LOG_M(ExcCode, WINS_EVT_RPC_EXC);
        DBGPRINT1(EXC, "WinsRecordAction: Got Exception (%x)\n", ExcCode);
        }
                  LeaveCriticalSection(&NmsDbOwnAddTblCrtSec);
                }

                pRecAction->NodeTyp  = StatInfo.NodeTyp;
                pRecAction->VersNo   = StatInfo.VersNo;
                pRecAction->fStatic  = StatInfo.fStatic;
              }
              else
              {
                  pRecAction->NoOfAdds = 0;
                  pRecAction->pAdd = NULL;
              }
              break;

        case(WINSINTF_E_MODIFY):

                //
                // Note: Currently, the administrator can not change the
                // address in the record
                //
                time((time_t *)&Rec.NewTimeStamp);

                //
                // If the record type is wrong, return a failure
                //
                if (pRecAction->TypOfRec_e > WINSINTF_E_MULTIHOMED)
                {
                        RetStat = WINS_FAILURE;
                        break;
                }
                //
                // If the state specified is wrong, return a failure
                //
                if (pRecAction->State_e > WINSINTF_E_DELETED)
                {
                        RetStat = WINS_FAILURE;
                        break;
                }
                NMSDB_SET_ENTRY_TYPE_M(Rec.Flag, pRecAction->TypOfRec_e);
                NMSDB_SET_NODE_TYPE_M(Rec.Flag, pRecAction->NodeTyp);
                NMSDB_SET_STDYN_M(Rec.Flag, pRecAction->fStatic);

                //
                // Fall through
                //

        case(WINSINTF_E_DELETE):
                NMSDB_SET_STATE_M(Rec.Flag, pRecAction->State_e)

                Rec.pName = pRecAction->pName;
                Rec.NameLen = pRecAction->NameLen + 1;

                //
                // NOTE:
                // The index on the name address table was set to
                // the clustered index column (as required by this function)
                // in NmsDbThdInit()
                //

                RetStat = NmsDbQueryNUpdIfMatch(
                                        &Rec,
                                        THREAD_PRIORITY_NORMAL,
                                        FALSE,  //don't change priority to
                                                //normal
                                        WINS_E_WINSRPC //ensures no matching
                                                       //of timestamps
                                                );
                if (RetStat == WINS_SUCCESS)
                {
                  DBGPRINT1(DET, "WinsRecordAction: Record (%s) deleted\n",
                                                  Rec.pName);
FUTURES("use macros defined in winsevt.h. Change to warning")
                  if (WinsCnf.LogDetailedEvts > 0)
                  {
                     WinsEvtLogDetEvt(TRUE, WINS_EVT_REC_DELETED, NULL, __LINE__, "s", Rec.pName);
                  }
                }
                break;


        default:
                RetStat = WINS_FAILURE;
                break;

  }
 } // end of try
 except(EXCEPTION_EXECUTE_HANDLER) {
        DWORD ExcCode = GetExceptionCode();
        WINSEVT_LOG_M(ExcCode, WINS_EVT_RPC_EXC);
        DBGPRINT1(EXC, "WinsRecordAction: Got Exception (%x)\n", ExcCode);
        }
  if (fSwapped)
  {
        WINS_SWAP_BYTES_M(pRecAction->pName, pRecAction->pName + 15);
  }
  //
  // Let us end the session
  //
  NmsDbCloseTables();
  NmsDbEndSession();
  if (RetStat == WINS_SUCCESS)
  {
      RetStat = WINSINTF_SUCCESS;
  }
  else
  {
      if (pRecAction->Cmd_e == WINSINTF_E_QUERY)
      {
             RetStat = WINSINTF_REC_NOT_FOUND;
      }
      else
      {
             RetStat = WINSINTF_FAILURE;
      }
  }
  return(RetStat);
}

DWORD
GetWinsStatus(
   IN  WINSINTF_CMD_E          Cmd_e,
   OUT LPVOID  pResults,
   BOOL fNew
        )

/*++

Routine Description:
        This function is called to get the information pertaining to WINS
        Refer WINSINTF_RESULTS_T data structure to see what information
        is retrieved

Arguments:
        Cmd_e    - Command to execute
        pResults -  Info. retrieved

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:
        R_WinsStatus()

Side Effects:

Comments:
        None
--*/

{

        DWORD RetVal = WINSINTF_FAILURE;

        switch(Cmd_e)
        {

                case(WINSINTF_E_ADDVERSMAP):
                                if (fNew)
                                {
                                   break;
                                }
                                RetVal = sGetVersNo(pResults);
                                break;
                case(WINSINTF_E_CONFIG):
                                RetVal = GetConfig(pResults, fNew, FALSE);
                                break;
                case(WINSINTF_E_CONFIG_ALL_MAPS):
                                RetVal = GetConfig(pResults, fNew, TRUE);
                                break;
                case(WINSINTF_E_STAT):
                                RetVal = GetStatistics(pResults, fNew);
                                break;
                default:
                  DBGPRINT1(ERR, "WinsStatus: Weird: Bad RPC Status command = (%D) \n", Cmd_e);
                  WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_BAD_RPC_STATUS_CMD);
                  break;

        }
        return(RetVal);

}

DWORD
WinsTrigger(
        PWINSINTF_ADD_T           pWinsAdd,
        WINSINTF_TRIG_TYPE_E         TrigType_e
        )

/*++

Routine Description:
        This function is called to send a trigger to a remote WINS so that
        it may pull the latest information from it

Arguments:

        pWinsAdd - Address of WINS to send a Push update notification to

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:
        R_WinsTrigger

Side Effects:

Comments:
        A Trigger is sent to a remote WINS only if it is specified
        under the PULL/PUSH subkey of the PARTNERS key in the registry
--*/

{

        PRPL_CONFIG_REC_T        pPnr;
        DWORD                        RetCode = WINSINTF_SUCCESS;
        BOOL                        fRplPnr = FALSE;
        QUE_CMD_TYP_E                CmdType_e;

        DBGENTER("WinsTrigger\n");
        //
        // Enter the critical sections guarded by WinsCnfCnfCrtSec and
        // NmsNmhNamRegCrtSec. There is no danger of deadlock because we
        // always enter the two critical sections in the following sequence
        //
        EnterCriticalSection(&WinsCnfCnfCrtSec);

PERF("Do we need to enter the following critical section")
//        EnterCriticalSection(&NmsNmhNamRegCrtSec);
try {
        if (
                (TrigType_e == WINSINTF_E_PUSH)
                        ||
                (TrigType_e == WINSINTF_E_PUSH_PROP)
              )
        {
                DBGPRINT1(DET, "WinsTrigger. Send Push trigger to (%x)\n",
                                     pWinsAdd->IPAdd);

                CmdType_e = (TrigType_e == WINSINTF_E_PUSH ?
                                QUE_E_CMD_SND_PUSH_NTF :
                                QUE_E_CMD_SND_PUSH_NTF_PROP);

                pPnr      = WinsCnf.PushInfo.pPushCnfRecs;
        }
        else        // it is a pull trigger
        {
                DBGPRINT1(DET, "WinsTrigger. Send Pull trigger to (%x)\n",
                                     pWinsAdd->IPAdd);

                CmdType_e  = QUE_E_CMD_REPLICATE;
                pPnr       = WinsCnf.PullInfo.pPullCnfRecs;

        }

        if (WinsCnf.fRplOnlyWCnfPnrs)
        {
           if (pPnr != NULL)
           {
              //
              // Search for the Cnf record for the WINS we want to
              // send the PUSH notification to/Replicate with.
              //
              for (
                                ;
                        (pPnr->WinsAdd.Add.IPAdd != INADDR_NONE)
                                        &&
                        !fRplPnr;
                                // no third expression
                  )
               {


                   DBGPRINT1(DET, "WinsTrigger. Comparing with (%x)\n",
                                     pPnr->WinsAdd.Add.IPAdd);
                   //
                   // Check if this is the one we want
                   //
                   if (pPnr->WinsAdd.Add.IPAdd == pWinsAdd->IPAdd)
                   {
                        //
                        // We are done.  Set the fRplPnr flag to TRUE so that
                        // we break out of the loop.
                        //
                        // Note: Don't use break since that would cause
                        // a search for a 'finally' block
                        //
                        fRplPnr = TRUE;

                        //
                        // Make it 0, so that we always try to establish
                        // a connection.  Otherwise, pull thread may not
                        // try if it has already exhausted the number of
                        // retries
                        //
                        pPnr->RetryCount = 0;
                        continue;                //so that we can break out

                   }
                   //
                   // Get the next record that follows this one sequentially
                   //
                   pPnr = WinsCnfGetNextRplCnfRec(
                                                pPnr,
                                                RPL_E_IN_SEQ   //seq. traversal
                                                   );
              } // end of for
          } // end of if (pPnr != 0)
       }  // end of if (fRplOnlyWCnfPnrs)
       else
       {
                //
                // Allocate from the general heap because that is what
                // is used by the replicator.
                //
                WinsMscAlloc(RPL_CONFIG_REC_SIZE, &pPnr);
                COMM_INIT_ADD_M(&pPnr->WinsAdd, pWinsAdd->IPAdd);
                pPnr->MagicNo           = 0;
                pPnr->RetryCount        = 0;
                pPnr->LastCommFailTime  = 0;
                pPnr->PushNtfTries    = 0;
                fRplPnr                     = TRUE;

                //
                // We want the buffer to be deallocated by the PULL thread
                //
                pPnr->fTemp = TRUE;
       }

       //
       // If replication needs to be done
       //
       if (fRplPnr)
       {
                //
                // Call RplInsertQue to insert the push request to
                // the Pull Thread
                //
                ERplInsertQue(
                             WINS_E_WINSRPC,
                             CmdType_e,
                             NULL,        //no Dlg Hdl
                             NULL,        //no msg is there
                             0,                //msg length
                             pPnr,   //client context
                             pPnr->MagicNo
                             );

       }
  } // end of try block

except (EXCEPTION_EXECUTE_HANDLER) {
                DWORD ExcCode = GetExceptionCode();
                DBGPRINT1(EXC, "WinsTrigger: Got Exception (%x)\n", ExcCode);
                WINSEVT_LOG_D_M(ExcCode, WINS_EVT_PUSH_TRIGGER_EXC);
                RetCode = WINSINTF_FAILURE;
  }

        //
        // Leave the critical section guarded by NmsNmhNamRegCrtSec.
        //
//        LeaveCriticalSection(&NmsNmhNamRegCrtSec);
        LeaveCriticalSection(&WinsCnfCnfCrtSec);

        //
        // if replication was allowed only with configured partners and
        // there was no WINS with the address specified by the client,
        // return failure
        //
        if (!fRplPnr)
        {
                RetCode = WINSINTF_RPL_NOT_ALLOWED;
        }

        DBGLEAVE("WinsTrigger\n");
        return(RetCode);
}

DWORD
sGetVersNo(
        LPVOID  pResultsA
        )

/*++

Routine Description:

        This function returns with the highest version number of records
        owned by a particular WINS

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{
        COMM_ADD_T        WinsAdd;
        DWORD                OwnerId;
        STATUS                RetStat;
        VERS_NO_T        VersNo;
        BOOL                fAllocNew = FALSE;
        PWINSINTF_RESULTS_T  pResults  = pResultsA;

        WinsAdd.AddLen    = sizeof(COMM_IP_ADD_T);
        WinsAdd.AddTyp_e  = COMM_ADD_E_TCPUDPIP;
        WinsAdd.Add.IPAdd = pResults->AddVersMaps[0].Add.IPAdd;
        RetStat = RplFindOwnerId(
                        &WinsAdd,
                        &fAllocNew,                //don't assign if not there
                        &OwnerId,
                        WINSCNF_E_IGNORE_PREC,
                        WINSCNF_LOW_PREC
                              );
        if(RetStat != WINS_SUCCESS)
        {
                return(WINSINTF_FAILURE);
        }

        if (OwnerId == 0)
        {
                EnterCriticalSection(&NmsNmhNamRegCrtSec);
                NMSNMH_DEC_VERS_NO_M(NmsNmhMyMaxVersNo, VersNo);
                LeaveCriticalSection(&NmsNmhNamRegCrtSec);
                pResults->AddVersMaps[0].VersNo = VersNo;
        }
        else
        {
           EnterCriticalSection(&RplVersNoStoreCrtSec);
           try {
                   pResults->AddVersMaps[0].VersNo =
                                        (pRplPullOwnerVersNo+OwnerId)->VersNo;
            }
            except(EXCEPTION_EXECUTE_HANDLER) {
                                DBGPRINTEXC("sGetVersNo");
                        }
            LeaveCriticalSection(&RplVersNoStoreCrtSec);
        }
        return(WINSINTF_SUCCESS);

}

DWORD
GetConfig(
        OUT  LPVOID  pResultsA,
        IN   BOOL    fNew,
        IN   BOOL    fAllMaps
        )

/*++

Routine Description:
        This function returns with configuration information
        and counter info related to replication

Arguments:
        pResults - has the information retrieved

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:
        GetWinsStatus()

Side Effects:

Comments:
        None
--*/

{

        PNMSDB_WINS_STATE_E pWinsState_e;
        PCOMM_ADD_T            pWinsAdd;
        PVERS_NO_T          pStartVersNo;
        DWORD                    i, n;
        VERS_NO_T            MyMaxVersNo;
        PWINSINTF_ADD_VERS_MAP_T pAddVersMaps, pAddVersMapsStore;
        PWINSINTF_RESULTS_T       pResults = pResultsA;
        PWINSINTF_RESULTS_NEW_T   pResultsN = pResultsA;
        BOOL                 fDel;
        VERS_NO_T            VersNoForDelRec;


        if (fAllMaps)
        {
          fDel = FALSE;
          VersNoForDelRec.HighPart = MAXLONG;
          VersNoForDelRec.LowPart  = MAXULONG;
        }
        EnterCriticalSection(&NmsNmhNamRegCrtSec);
        MyMaxVersNo = NmsNmhMyMaxVersNo;
        LeaveCriticalSection(&NmsNmhNamRegCrtSec);

        if (fNew)
        {

             pResultsN->pAddVersMaps =
                     midl_user_allocate(NmsDbNoOfOwners * sizeof(WINSINTF_ADD_VERS_MAP_T));
             pAddVersMaps = pResultsN->pAddVersMaps;

        }
        else
        {
             pAddVersMaps = pResults->AddVersMaps;
        }
        pAddVersMapsStore = pAddVersMaps;
        //
        // First extract the timeouts and Non-remote WINS information
        // from the WinsCnf global var.  We enter the WinsCnfCnfCrtSec
        // since we need to synchronize with the thread doing the
        // reinitialization (Main thread)
        //
        EnterCriticalSection(&WinsCnfCnfCrtSec);
        if (!fNew)
        {
        pResults->RefreshInterval    = WinsCnf.RefreshInterval;
        pResults->TombstoneInterval  = WinsCnf.TombstoneInterval;
        pResults->TombstoneTimeout   = WinsCnf.TombstoneTimeout;
        pResults->VerifyInterval     = WinsCnf.VerifyInterval;
        pResults->WinsPriorityClass  = WinsCnf.WinsPriorityClass == (DWORD)WINSINTF_E_NORMAL ? NORMAL_PRIORITY_CLASS : HIGH_PRIORITY_CLASS;
        pResults->NoOfWorkerThds     = NmsNoOfNbtThds;
        }
        else
        {
        pResultsN->RefreshInterval    = WinsCnf.RefreshInterval;
        pResultsN->TombstoneInterval  = WinsCnf.TombstoneInterval;
        pResultsN->TombstoneTimeout   = WinsCnf.TombstoneTimeout;
        pResultsN->VerifyInterval     = WinsCnf.VerifyInterval;
        pResultsN->WinsPriorityClass  = WinsCnf.WinsPriorityClass == (DWORD)WINSINTF_E_NORMAL ? NORMAL_PRIORITY_CLASS : HIGH_PRIORITY_CLASS;
        pResultsN->NoOfWorkerThds     = NmsNoOfNbtThds;

        }
        LeaveCriticalSection(&WinsCnfCnfCrtSec);

        //
        // Enter two critical sections in sequence. No danger of deadlock
        // here. The only other thread that takes both these critical section
        // is the RplPush thread. It takes these in the same order (See
        // HandleVersMapReq())
        //
        EnterCriticalSection(&NmsDbOwnAddTblCrtSec);
        EnterCriticalSection(&RplVersNoStoreCrtSec);
try {
NONPORT("Change when using different address family")

         for (
                i=0, n = 0;
                i < NmsDbNoOfOwners;
                i++
             )
         {

                if (!fNew && (i == WINSINTF_MAX_NO_RPL_PNRS))
                {
                    break;
                }
                //
                // if the record is deleted in the in-memory table, it
                // means that there are no records for it in the
                // database
                //
                if ((pNmsDbOwnAddTbl+i)->WinsState_e == NMSDB_E_WINS_DELETED)
                {
                        //
                        // if only active mappings are sought, skip this
                        // entry
                        //
                        if (!fAllMaps)
                        {
                          continue;
                        }
                        else
                        {
                             fDel = TRUE;
                        }
                }

                //
                // Find address corresponding to the owner id.
                //
                RPL_FIND_ADD_BY_OWNER_ID_M(i, pWinsAdd, pWinsState_e,
                                                pStartVersNo);

                //
                //  It is possible for NmsDbNoOfOwners to be more than the
                //  number of initialized RplPullVersNoTbl entries.  When
                //  we reach a NULL entry, we break out of the loop.
                //
                if (pWinsAdd != NULL)
                {
                        pAddVersMaps->Add.Type   = WINSINTF_TCP_IP;
                        pAddVersMaps->Add.Len    = pWinsAdd->AddLen;
                        pAddVersMaps->Add.IPAdd  =  pWinsAdd->Add.IPAdd;

                        pAddVersMaps++->VersNo =  (fDel == FALSE) ? (pRplPullOwnerVersNo+i)->VersNo : VersNoForDelRec;
                }
                else
                {
                        break;
                }
                if (fDel)
                {
                    fDel = FALSE;
                }
                n++;
         } // end of for ..

         //
         // Since RplPullOwnerVersNo[0] may be out of date, let us get
         // get the uptodate value
         //
         NMSNMH_DEC_VERS_NO_M(MyMaxVersNo,
                              pAddVersMapsStore->VersNo
                             );
         if (fNew)
         {
           pResultsN->NoOfOwners = n;
         }
         else
         {
           pResults->NoOfOwners = n;

         }
NOTE("Wins Mib agent relies on the first entry being that of the local WINS")
NOTE("See WinsMib.c -- EnumAddKeys")
#if 0
         //
         // Since RplPullOwnerVersNo[0] may be out of date, let us get
         // get the uptodate value
         //
         NMSNMH_DEC_VERS_NO_M(MyMaxVersNo,
                              pResults->AddVersMaps[0].VersNo
                             );
#endif
  }
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("GetConfig");
        //
        // log a message
        //
 }
        LeaveCriticalSection(&RplVersNoStoreCrtSec);
        LeaveCriticalSection(&NmsDbOwnAddTblCrtSec);

        return(WINSINTF_SUCCESS);
}

DWORD
GetStatistics(
        LPVOID  pResultsA,
        BOOL                 fNew
        )

/*++

Routine Description:

        This function returns with the highest version number of records
        owned by a particular WINS

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{

        PRPL_CONFIG_REC_T         pPnr;
        PWINSINTF_RPL_COUNTERS_T  pPnrData;
        DWORD                     i;
        PWINSINTF_RESULTS_T       pResults = pResultsA;
        PWINSINTF_RESULTS_NEW_T   pResultsN = pResultsA;


        ASSERT(pResults != NULL);

        
        //
        // If client passed us a non-null pRplPnr, then we return failure
        // so as to avoid a memory leak.  
        //
        if (!fNew)
        {
              if (pResults->WinsStat.pRplPnrs != NULL)
              {
                    return(WINSINTF_FAILURE);
              }

        }
        else
        {

              if (pResultsN->WinsStat.pRplPnrs != NULL)
              {
                    return(WINSINTF_FAILURE);
              }
        }
        //
        // Copy the counters
        //
        EnterCriticalSection(&NmsNmhNamRegCrtSec);
        if (!fNew)
        {
        pResults->WinsStat.Counters = WinsIntfStat.Counters;
FUTURES("Get rid of of the following two fields")
        pResults->WinsStat.Counters.NoOfQueries =
                                WinsIntfStat.Counters.NoOfSuccQueries +
                                  WinsIntfStat.Counters.NoOfFailQueries;
        pResults->WinsStat.Counters.NoOfRel = WinsIntfStat.Counters.NoOfSuccRel
                                        + WinsIntfStat.Counters.NoOfFailRel;
        }
        else
        {
        pResultsN->WinsStat.Counters = WinsIntfStat.Counters;
FUTURES("Get rid of of the following two fields")
        pResultsN->WinsStat.Counters.NoOfQueries =
                                WinsIntfStat.Counters.NoOfSuccQueries +
                                  WinsIntfStat.Counters.NoOfFailQueries;
        pResultsN->WinsStat.Counters.NoOfRel = WinsIntfStat.Counters.NoOfSuccRel
                                        + WinsIntfStat.Counters.NoOfFailRel;

        }
        LeaveCriticalSection(&NmsNmhNamRegCrtSec);

        //
        // Copy the TimeStamps and replication specific counters
        //
        EnterCriticalSection(&WinsIntfCrtSec);
        {
            PWINSINTF_STAT_T pWinsStat = (fNew) ? &(pResultsN->WinsStat) : &(pResults->WinsStat);
            TIME_ZONE_INFORMATION tzInfo;

            GetTimeZoneInformation(&tzInfo);
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.WinsStartTime), &(pWinsStat->TimeStamps.WinsStartTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastPScvTime), &(pWinsStat->TimeStamps.LastPScvTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastATScvTime), &(pWinsStat->TimeStamps.LastATScvTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastTombScvTime), &(pWinsStat->TimeStamps.LastTombScvTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastVerifyScvTime), &(pWinsStat->TimeStamps.LastVerifyScvTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastInitDbTime), &(pWinsStat->TimeStamps.LastInitDbTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastPRplTime), &(pWinsStat->TimeStamps.LastPRplTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastATRplTime), &(pWinsStat->TimeStamps.LastATRplTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastNTRplTime), &(pWinsStat->TimeStamps.LastNTRplTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.LastACTRplTime), &(pWinsStat->TimeStamps.LastACTRplTime));
            SystemTimeToTzSpecificLocalTime(&tzInfo, &(WinsIntfStat.TimeStamps.CounterResetTime), &(pWinsStat->TimeStamps.CounterResetTime));
        }
        LeaveCriticalSection(&WinsIntfCrtSec);
        EnterCriticalSection(&WinsCnfCnfCrtSec);
try {
        DWORD  NoOfPnrs;
        pPnr       = WinsCnf.PullInfo.pPullCnfRecs;
        if (!fNew)
        {
          NoOfPnrs = pResults->WinsStat.NoOfPnrs = WinsCnf.PullInfo.NoOfPushPnrs;

        }
        else
        {
          NoOfPnrs = pResultsN->WinsStat.NoOfPnrs = WinsCnf.PullInfo.NoOfPushPnrs;
        }

        //
        // If no. of push pnrs (pnrs under the pull key) is > 0
        //
        if (NoOfPnrs > 0)
        {
           if (!fNew)
           {
             pPnrData = pResults->WinsStat.pRplPnrs =
                        midl_user_allocate(pResults->WinsStat.NoOfPnrs *
                                         sizeof(WINSINTF_RPL_COUNTERS_T));
           }
           else
           {
             pPnrData = pResultsN->WinsStat.pRplPnrs =
                        midl_user_allocate(pResultsN->WinsStat.NoOfPnrs *
                                         sizeof(WINSINTF_RPL_COUNTERS_T));

           }
PERF("remove one of the expressions in the test condition of the if statement")
           for (
                 i = 0;
                (pPnr->WinsAdd.Add.IPAdd != INADDR_NONE)
                        &&
                i < NoOfPnrs;
                pPnrData++, i++
               )
           {

                   pPnrData->Add.IPAdd     = pPnr->WinsAdd.Add.IPAdd;
                   (VOID)InterlockedExchange(
                                        &pPnrData->NoOfRpls, pPnr->NoOfRpls);
                   (VOID)InterlockedExchange(
                                        &pPnrData->NoOfCommFails,
                                        pPnr->NoOfCommFails
                                        );
                   //
                   // Get the next record that follows this one sequentially
                   //
                   pPnr = WinsCnfGetNextRplCnfRec(
                                                pPnr,
                                                RPL_E_IN_SEQ   //seq. traversal
                                                   );
           }
        }
        else
        {
           if (!fNew)
           {
               pResults->WinsStat.pRplPnrs = NULL;
           }
           else
           {
               pResultsN->WinsStat.pRplPnrs = NULL;
           }
        }
}
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("GetStatistics");
        //
        // log a message
        //
        }

        LeaveCriticalSection(&WinsCnfCnfCrtSec);

        GetConfig(pResultsA, fNew, FALSE);
        return(WINSINTF_SUCCESS);
} // GetStatistics


DWORD
WinsDoStaticInit(
        LPWSTR  pDataFilePath,
        DWORD   fDel
        )

/*++

Routine Description:
        This function does the STATIC initialization of WINS

Arguments:
        pDataFilePath - Path to the data file or NULL

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:
        R_WinsDoStaticInit
Side Effects:

Comments:
        None
--*/

{

        WINSCNF_CNF_T        WinsCnf;
        STATUS               RetStat = WINS_SUCCESS;

        //
        // If no path has been specified, take the values from the registry
        //
        if (pDataFilePath == NULL)
        {
           //
           // Read the DataFiles info information
           //
           // This function will return with either the name of the default
           // file to read or one or more files specified under the
           // Parameters\Datafiles key of WINS
           //
           (VOID)WinsCnfGetNamesOfDataFiles(&WinsCnf);
        }
        else
        {
FUTURES("expensive.  Change idl prototype to pass length")
                if (lstrlen(pDataFilePath) >= WINS_MAX_FILENAME_SZ)
                {

                        return(WINSINTF_STATIC_INIT_FAILED);
                }
                //
                // Set time of data initialization
                //
                WinsIntfSetTime(NULL, WINSINTF_E_INIT_DB);
                WinsMscAlloc(WINSCNF_FILE_INFO_SZ, &WinsCnf.pStaticDataFile);

                lstrcpy(WinsCnf.pStaticDataFile->FileNm,  pDataFilePath);
                WinsCnf.pStaticDataFile->StrType = REG_EXPAND_SZ;
                WinsCnf.NoOfDataFiles = 1;
        }

        //
        // If Static initialization fails, it will be logged.
        // This function does not return an error code
        //
        if ((RetStat = WinsPrsDoStaticInit(
                          WinsCnf.pStaticDataFile,
                          WinsCnf.NoOfDataFiles,
                           FALSE                   //do it synchronously
                                   )) == WINS_SUCCESS)
        {
           if ((pDataFilePath != NULL) && fDel)
           {
             if (!DeleteFile(pDataFilePath))
             {
                DWORD Error;
                Error = GetLastError();
                if (Error != ERROR_FILE_NOT_FOUND)
                {
                    DBGPRINT1(ERR, "DbgOpenFile: Could not delete the data file. Error = (%d).  Dbg file will not be truncated\n", Error);
                    WinsEvtLogDetEvt(FALSE, WINS_EVT_COULD_NOT_DELETE_FILE,
                      TEXT("winsintf.c"), __LINE__, "ud", pDataFilePath, Error);
                    RetStat = Error;

                }
             }

           }
        }
        return (RetStat == WINS_SUCCESS ? WINSINTF_SUCCESS : WINSINTF_STATIC_INIT_FAILED);
}
DWORD
WinsDoScavenging(
        VOID
        )

/*++

Routine Description:
        This function starts the scavenging cycle

Arguments:
        None

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  exception from WinsMscSignalHdl

Error Handling:

Called by:
        R_WinsDoScavenging
Side Effects:

Comments:
        None
--*/

{
        PQUE_SCV_REQ_WRK_ITM_T pWrkItm;

        pWrkItm = WinsMscHeapAlloc( NmsRpcHeapHdl, sizeof(QUE_SCV_REQ_WRK_ITM_T));
        pWrkItm->Opcode_e = WINSINTF_E_SCV_GENERAL;
        pWrkItm->CmdTyp_e = QUE_E_CMD_SCV_ADMIN;
        pWrkItm->fForce   = 0;
        pWrkItm->Age      = 1;   //should not be zero since zero implies
                                 //consistency check on all replicas.
        WinsLogAdminEvent( WINS_EVT_ADMIN_SCVENGING_INITIATED, 0 );
        QueInsertScvWrkItm((PLIST_ENTRY)pWrkItm);
         return (WINSINTF_SUCCESS);
}

DWORD
WinsDoScavengingNew(
    PWINSINTF_SCV_REQ_T  pScvReq
        )

/*++

Routine Description:
        This function starts the scavenging cycle

Arguments:
        None

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  exception from WinsMscSignalHdl

Error Handling:

Called by:
        R_WinsDoScavenging
Side Effects:

Comments:
        None
--*/

{
        PQUE_SCV_REQ_WRK_ITM_T pWrkItm;
        pWrkItm = WinsMscHeapAlloc( NmsRpcHeapHdl, sizeof(QUE_SCV_REQ_WRK_ITM_T));
        pWrkItm->Opcode_e = pScvReq->Opcode_e;
        pWrkItm->Age      = pScvReq->Age;
        pWrkItm->fForce   = pScvReq->fForce;
        if (WINSINTF_E_SCV_GENERAL == pWrkItm->Opcode_e ) {
            WinsLogAdminEvent( WINS_EVT_ADMIN_SCVENGING_INITIATED, 0 );
        } else {
            WinsLogAdminEvent( WINS_EVT_ADMIN_CCCHECK_INITIATED, 0);
        }
        QueInsertScvWrkItm((PLIST_ENTRY)pWrkItm);
         return (WINSINTF_SUCCESS);
}

DWORD
WinsGetDbRecs (
        PWINSINTF_ADD_T             pWinsAdd,
        WINSINTF_VERS_NO_T          MinVersNo,
        WINSINTF_VERS_NO_T          MaxVersNo,
        PWINSINTF_RECS_T          pRecs
        )

/*++

Routine Description:
        This function returns with all the records (that can fit into the
        buffer passed) owned by a WINS in the local db of this WINS.

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{
        COMM_ADD_T           Address;
        PRPL_REC_ENTRY_T     pBuff = NULL;
        LPVOID               pStartBuff;
        DWORD                BuffLen;
        DWORD                NoOfRecs;
        PWINSINTF_RECORD_ACTION_T pRow;
        DWORD                i;
        DWORD                ind;
        //VERS_NO_T          MinVersNo = {0,0};
        DWORD                EntType;
        PWINSTHD_TLS_T       pTls;
        //ULONG                Status;
        BOOL                 fExcRaised = FALSE;


//   PVOID pCallersAdd, pCallersCaller;
//   RtlGetCallersAddress(&pCallersAdd, &pCallersCaller);
        DBGENTER("WinsGetDbRecs\n");

        if (LiLtr(MaxVersNo, MinVersNo))
        {
                return(WINSINTF_FAILURE);
        }

        Address.AddTyp_e  = pWinsAdd->Type;
        Address.AddLen    = pWinsAdd->Len;

        //
        // snmp agent can pass a 0 for the address to ask for all records
        // owned by the local wins.
        //
        if (pWinsAdd->IPAdd == 0)
        {
                  Address.AddTyp_e  = NmsLocalAdd.AddTyp_e;
                  Address.AddLen    = NmsLocalAdd.AddLen;
                  Address.Add.IPAdd = NmsLocalAdd.Add.IPAdd;
        }
        else
        {
                  Address.AddTyp_e  = pWinsAdd->Type;
                  Address.AddLen    = pWinsAdd->Len;
                  Address.Add.IPAdd = pWinsAdd->IPAdd;
        }
        //
        // initialize this thread with the db engine
        //
        NmsDbThdInit(WINS_E_WINSRPC);
        NmsDbOpenTables(WINS_E_WINSRPC);
        DBGMYNAME("RPC-WinsGetDbRecs");

PERF("The caller can pass the number of records for which space has been")
PERF("allocated in buffer pointed to by pRec in the NoOfRecs field. We should")
PERF("We should pass this argument to NmsDbGetDataRecs so that it does not get")
PERF("more records than are necessary")

        GET_TLS_M(pTls);
try {
        NmsDbGetDataRecs(
                        WINS_E_WINSRPC,
                        0,                //not used
                        MinVersNo,
                        MaxVersNo,
                        0,                //not used
                        LiEqlZero(MinVersNo) && LiEqlZero(MaxVersNo) ? TRUE : FALSE,
                        FALSE,                //not used
                        NULL,                //must be NULL since we are not doing
                                        //scavenging of clutter
                        &Address,
                        FALSE,          //dynamic + static records are wanted
//#if RPL_TYPE
                        WINSCNF_RPL_DEFAULT_TYPE,
//#endif
                        &pBuff,
                        &BuffLen,
                        &NoOfRecs
                        );

        i = 0;
        pStartBuff       = pBuff;

        //
        // If there are records to send back and the client has specified
        // a buffer for them, insert the records
        //
        if  (NoOfRecs > 0)
        {

          //
          // Allocate memory for the no of records
          //
          pRecs->BuffSize  =  sizeof(WINSINTF_RECORD_ACTION_T) * NoOfRecs;

          //
          // If memory can not be allocate, an exception will be returned
          // by midl_user_alloc
          //
          pRecs->pRow      =  midl_user_allocate(pRecs->BuffSize);
//          DBGPRINT1(DET, "WinsGetDbRecs: Address of memory for records is (%d)\n", pRecs->pRow);

#if 0
          pRecs->pRow      =  RpcSmAllocate(pRecs->BuffSize, &Status);
          if (Status != RPC_S_OK)
          {
             DBGPRINT1(ERR, "WinsGetDbRecs: RpcSmAllocate returned error = (%x)\n", Status);
          }
#endif
          pRow                    =  pRecs->pRow;


          for (; i<NoOfRecs; i++)
          {

                //
                // Initialize so that we don't get "enum wrong" error.
                //
                pRow->Cmd_e = WINSINTF_E_QUERY;

                //
                // the name retrieved has NULL as the last character.  This
                // We need to pass a name without this NULL.
                //
                pRow->NameLen = pBuff->NameLen;
                if (*pBuff->pName == 0x1B)
                {
                        WINS_SWAP_BYTES_M(pBuff->pName, pBuff->pName + 15);
                }

                pRow->pName =  midl_user_allocate(pRow->NameLen + 1);
                //DBGPRINT2(DET, "WinsGetDbRecs: Address of name = (%s) is (%d) \n", pBuff->pName, pRow->pName);
#if 0
                pRow->pName =  RpcSmAllocate(pRow->NameLen, &Status);
                if (Status != RPC_S_OK)
                {
                         DBGPRINT1(ERR, "WinsGetDbRecs: RpcSmAllocate returned error = (%x)\n", Status);
                }
#endif
                WINSMSC_COPY_MEMORY_M(pRow->pName, pBuff->pName,
                                                pRow->NameLen);


                WinsMscHeapFree(pTls->HeapHdl, pBuff->pName);
                EntType = NMSDB_ENTRY_TYPE_M(pBuff->Flag);
                pRow->TypOfRec_e = NMSDB_ENTRY_UNIQUE_M(EntType)
                                                         ? WINSINTF_E_UNIQUE :
                                (NMSDB_ENTRY_NORM_GRP_M(EntType) ?
                                        WINSINTF_E_NORM_GROUP :
                                (NMSDB_ENTRY_SPEC_GRP_M(EntType) ?
                                        WINSINTF_E_SPEC_GROUP :
                                        WINSINTF_E_MULTIHOMED));


                if (
                    (pRow->TypOfRec_e == WINSINTF_E_SPEC_GROUP) ||
                    (pRow->TypOfRec_e == WINSINTF_E_MULTIHOMED)
                   )
                {
                    PWINSINTF_ADD_T        pAdd;
                    DWORD                 No;
                    if (pBuff->NoOfAdds > 0)
                    {
                        pRow->NoOfAdds = pBuff->NoOfAdds * 2;

                        //
                        // Each member is comprised of two addresses,
                        // first address is that of the owner WINS, second
                        // address is that of the node registered
                        //
                        pRow->pAdd        =
//                             RpcSmAllocate(
                             midl_user_allocate(
                                        (unsigned int)(pRow->NoOfAdds)
                                                *
                                        sizeof(WINSINTF_ADD_T)//,
//                                        &Status
                                             );
                        //DBGPRINT2(DET, "WinsGetDbRecs: Address of ip address for name = (%s) is (%d) \n", pRow->pName, pRow->pAdd);
#if 0
                        if (Status != RPC_S_OK)
                        {
                             DBGPRINT1(ERR, "WinsGetDbRecs: RpcSmAllocate returned error = (%x)\n", Status);
                        }
#endif
                        for (
                                No= 0, ind= 0, pAdd = pRow->pAdd;
                                No < (pRow->NoOfAdds/2);
                                No++
                            )
                        {
                          pAdd->Type     =  (UCHAR)(pBuff->pNodeAdd + ind)->AddTyp_e;
                          pAdd->Len      =  (pBuff->pNodeAdd + ind)->AddLen;
                          pAdd++->IPAdd  =  (pBuff->pNodeAdd + ind)->Add.IPAdd;

                          pAdd->Type     =  (UCHAR)(pBuff->pNodeAdd + ++ind)->AddTyp_e;
                          pAdd->Len      =  (pBuff->pNodeAdd + ind)->AddLen;
                          pAdd++->IPAdd  =  (pBuff->pNodeAdd + ind++)->Add.IPAdd;
                        }
                        WinsMscHeapFree(pTls->HeapHdl, pBuff->pNodeAdd);
                    }
                }
                else
                {
                          pRow->NoOfAdds   = 0;
                          pRow->pAdd       = NULL;
                          pRow->Add.Type   = (UCHAR)pBuff->NodeAdd[0].AddTyp_e;
                          pRow->Add.Len    = pBuff->NodeAdd[0].AddLen;
                          pRow->Add.IPAdd  = pBuff->NodeAdd[0].Add.IPAdd;
                }
                pRow->NodeTyp     = (BYTE)NMSDB_NODE_TYPE_M(pBuff->Flag);
                pRow->fStatic     = NMSDB_IS_ENTRY_STATIC_M(pBuff->Flag);
                pRow->State_e     = NMSDB_ENTRY_STATE_M(pBuff->Flag);
                pRow->VersNo      = pBuff->VersNo;
                pRow->TimeStamp   = pBuff->TimeStamp;

                pRow++;


                pBuff = (PRPL_REC_ENTRY_T)((LPBYTE)pBuff + RPL_REC_ENTRY_SIZE);
PERF("Do the addition above the for loop and store in a var. Use var. here")

         } // end of for loop
        } //end of if block
        else
        {
                pRecs->pRow = NULL;
        }

 }        // end of try
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsGetDbRecs");
        WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_RPC_EXC);
        fExcRaised = TRUE;
        }

        pRecs->TotalNoOfRecs = NoOfRecs;

        //
        // Deallocate the buffer allocated by NmsDbGetDataRecs
        //


        WinsMscHeapFree(pTls->HeapHdl, pStartBuff);
        WinsMscHeapDestroy(pTls->HeapHdl);

        if (!fExcRaised)
        {
           pRecs->NoOfRecs = i;
        }
        else
        {
//             RpcSmFree(pRecs->pRow);
            midl_user_free(pRecs->pRow);
            pRecs->NoOfRecs = 0;
        }

        //
        // Let us end the session
        //
        NmsDbCloseTables();
        NmsDbEndSession();

        DBGLEAVE("WinsGetDbRecs\n");
        return (WINSINTF_SUCCESS);
}

VOID
WinsIntfSetTime(
        OUT PSYSTEMTIME                     pTime,
        IN     WINSINTF_TIME_TYPE_E        TimeType_e
        )

/*++

Routine Description:
        This function is called to set the the time in the WINSINTF_STAT_T
        structure

Arguments:
        pTime      - Local Time (returned)
        TimeType_e - The activity for which the time has to be stored

Externals Used:
        None

Return Value:

        NONE

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/
{
        SYSTEMTIME     SysTime;
        PSYSTEMTIME    pSysTime = &SysTime;

        GetSystemTime(pSysTime);

        EnterCriticalSection(&WinsIntfCrtSec);

        switch(TimeType_e)
        {
         case(WINSINTF_E_WINS_START):
                WinsIntfStat.TimeStamps.WinsStartTime   = *pSysTime;
                break;
         case(WINSINTF_E_PLANNED_SCV):
                WinsIntfStat.TimeStamps.LastPScvTime = *pSysTime;
                break;
         case(WINSINTF_E_ADMIN_TRIG_SCV):
                WinsIntfStat.TimeStamps.LastATScvTime = *pSysTime;
                break;
         case(WINSINTF_E_TOMBSTONES_SCV):
                WinsIntfStat.TimeStamps.LastTombScvTime = *pSysTime;
                break;
         case(WINSINTF_E_VERIFY_SCV):
                WinsIntfStat.TimeStamps.LastVerifyScvTime = *pSysTime;
                break;
         case(WINSINTF_E_INIT_DB):
                WinsIntfStat.TimeStamps.LastInitDbTime = *pSysTime;
                break;
         case(WINSINTF_E_PLANNED_PULL):
                WinsIntfStat.TimeStamps.LastPRplTime = *pSysTime;
                break;
         case(WINSINTF_E_ADMIN_TRIG_PULL):
                WinsIntfStat.TimeStamps.LastATRplTime = *pSysTime;
                break;
         case(WINSINTF_E_NTWRK_TRIG_PULL):
                WinsIntfStat.TimeStamps.LastNTRplTime = *pSysTime;
                break;
         case(WINSINTF_E_UPDCNT_TRIG_PULL):
                WinsIntfStat.TimeStamps.LastNTRplTime = *pSysTime;
                break;
         case(WINSINTF_E_ADDCHG_TRIG_PULL):
                WinsIntfStat.TimeStamps.LastACTRplTime = *pSysTime;
                break;
         case(WINSINTF_E_COUNTER_RESET):
                WinsIntfStat.TimeStamps.CounterResetTime   = *pSysTime;
                break;
         default:
                DBGPRINT1(EXC, "WinsIntfSetTime: Weird Timestamp type = (%d)\n", TimeType_e);
                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
                break;
        }
        LeaveCriticalSection(&WinsIntfCrtSec);

        if (pTime)
        {
            TIME_ZONE_INFORMATION tzInfo;
            GetTimeZoneInformation(&tzInfo);
            SystemTimeToTzSpecificLocalTime(&tzInfo, pSysTime, pTime);
        }

        return;
}

DWORD
WinsDelDbRecs(
        IN PWINSINTF_ADD_T        pAdd,
        IN WINSINTF_VERS_NO_T        MinVersNo,
        IN WINSINTF_VERS_NO_T        MaxVersNo
        )

/*++

Routine Description:
        This func. deletes a specified range of records belonging to a
        particular owner

Arguments:


Externals Used:
        None


Return Value:

   Success status codes -- WINSINTF_SUCCESS
   Error status codes   -- WINSINTF_FAILURE

Error Handling:

Called by:
        R_WinsDelDbRecs

Side Effects:

Comments:
        None
--*/

{

        COMM_ADD_T        Address;
        DWORD                RetVal = WINSINTF_SUCCESS;
        DWORD                dwOwnerId;
        BOOL                fAllocNew = FALSE;

          Address.AddTyp_e  = pAdd->Type;
          Address.AddLen    = pAdd->Len;
          Address.Add.IPAdd = pAdd->IPAdd;

        //
        // initialize this thread with the db engine
        //
          NmsDbThdInit(WINS_E_WINSRPC);
        NmsDbOpenTables(WINS_E_WINSRPC);
        DBGMYNAME("RPC-WinsDelDbRecs");


        if (RplFindOwnerId(
                        &Address,
                        &fAllocNew,        //do not allocate an entry if not
                                        //present
                        &dwOwnerId,
                        WINSCNF_E_IGNORE_PREC,
                        WINSCNF_LOW_PREC
                      ) != WINS_SUCCESS)

        {
                DBGPRINT0(DET, "WinsDelDataRecs: WINS is not in the owner-add mapping table\n");
                RetVal = WINSINTF_FAILURE;
        }
        else
        {
            if (NmsDbDelDataRecs(
                        dwOwnerId,
                        MinVersNo,
                        MaxVersNo,
                        TRUE,         //enter critical section
                        FALSE         //no fragmented deletion
                        ) != WINS_SUCCESS)
            {
                RetVal = WINSINTF_FAILURE;
            }
        }

          //
          // Let us end the session
          //
        NmsDbCloseTables();
          NmsDbEndSession();
        return(RetVal);
}

DWORD
WinsTombstoneDbRecs(
        IN PWINSINTF_ADD_T           pAdd,
        IN WINSINTF_VERS_NO_T        MinVersNo,
        IN WINSINTF_VERS_NO_T        MaxVersNo
        )

/*++

Routine Description:
        This func. tombstones a specified range of records belonging to a
        particular owner

Arguments:


Externals Used:
        None


Return Value:

   Success status codes -- WINSINTF_SUCCESS
   Error status codes   -- WINSINTF_FAILURE

Error Handling:

Called by:
        R_WinsTombstoneDbRecs

Side Effects:


Comments:
        None
--*/

{

        DWORD                RetVal = WINSINTF_SUCCESS;
        COMM_ADD_T        Address;
        DWORD                dwOwnerId;
        BOOL                fAllocNew = FALSE;

        Address.AddTyp_e  = pAdd->Type;
        Address.AddLen    = pAdd->Len;
        Address.Add.IPAdd = pAdd->IPAdd;


        //
        // initialize this thread with the db engine
        //
        NmsDbThdInit(WINS_E_WINSRPC);
        NmsDbOpenTables(WINS_E_WINSRPC);
        DBGMYNAME("RPC-WinsTombstoneDbRecs");

        if (RplFindOwnerId(
                        &Address,
                        &fAllocNew,        //do not allocate an entry if not
                                        //present
                        &dwOwnerId,
                        WINSCNF_E_IGNORE_PREC,
                        WINSCNF_LOW_PREC
                      ) != WINS_SUCCESS)

        {
                DBGPRINT0(DET, "WinsTombstoneDataRecs: WINS is not in the owner-add mapping table\n");
                RetVal = WINSINTF_FAILURE;
        }else if(NmsDbTombstoneDataRecs(
                    dwOwnerId,
                    MinVersNo,
                    MaxVersNo
                    ) != WINS_SUCCESS)
        {
            RetVal = WINSINTF_FAILURE;
        }
        // Let us end the session
        NmsDbCloseTables();
        NmsDbEndSession();
        return(RetVal);
}

DWORD
WinsPullRange(
        IN PWINSINTF_ADD_T        pWinsAdd,
        IN PWINSINTF_ADD_T        pOwnerAdd,
        IN WINSINTF_VERS_NO_T        MinVersNo,
        IN WINSINTF_VERS_NO_T        MaxVersNo
        )

/*++

Routine Description:
        This function is called to pull a range of records owned by a particular
        WINS server from another WINS server.

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{
        PWINSINTF_PULL_RANGE_INFO_T pPullRangeInfo;
        PRPL_CONFIG_REC_T        pPnr;
        BOOL                        fRplPnr = FALSE;
        DWORD                        RetCode = WINSINTF_SUCCESS;


        //
        // if the minimum version number is > than the max version number
        // return a failure code
        //
        if (LiGtr(MinVersNo, MaxVersNo))
        {
                return(WINSINTF_FAILURE);

        }
        //
        // Enter the critical sections guarded by WinsCnfCnfCrtSec and
        // NmsNmhNamRegCrtSec. There is no danger of deadlock because we
        // always enter the two critical sections in the following sequence
        //
        EnterCriticalSection(&WinsCnfCnfCrtSec);

PERF("Do we need to enter the following critical section")
        EnterCriticalSection(&NmsNmhNamRegCrtSec);
try {
        pPnr = WinsCnf.PullInfo.pPullCnfRecs;

        //
        // If we are allowed to pull only from configured partners,
        // let us try to find the config record of the partner
        //
        if (WinsCnf.fRplOnlyWCnfPnrs)
        {
           if (pPnr != NULL)
           {
              //
              // Search for the Cnf record for the WINS we want to
              // send the PULL RANGE request to.
              //
              for (
                                ;
                        (pPnr->WinsAdd.Add.IPAdd != INADDR_NONE)
                                        &&
                        !fRplPnr;
                                // no third expression
                  )
               {


                   //
                   // Check if this is the one we want
                   //
                   if (pPnr->WinsAdd.Add.IPAdd == pWinsAdd->IPAdd)
                   {
                        //
                        // We are done.  Set the fRplPnr flag to TRUE so that
                        // we break out of the loop.
                        //
                        fRplPnr = TRUE;

                        //
                        // Make it 0, so that we always try to establish
                        // a connection.  Otherwise, pull thread may not
                        // try if it has already exhausted the number of
                        // retries
                        //
                        pPnr->RetryCount = 0;
                        continue;                //so that we can break out

                   }
                   //
                   // Get the next record that follows this one sequentially
                   //
                   pPnr = WinsCnfGetNextRplCnfRec(
                                                pPnr,
                                                RPL_E_IN_SEQ   //seq. traversal
                                                   );
              } // end of for
          } // end of if (pPnr != 0)
       }  // end of if (fRplOnlyWCnfPnrs)
       else
       {
                pPnr = WinsMscHeapAlloc( NmsRpcHeapHdl, RPL_CONFIG_REC_SIZE);
                COMM_INIT_ADD_M(&pPnr->WinsAdd, pWinsAdd->IPAdd);
                pPnr->MagicNo           = 0;
                pPnr->RetryCount        = 0;
                pPnr->LastCommFailTime  = 0;
                pPnr->PushNtfTries    = 0;
                fRplPnr                     = TRUE;

                //
                // We want the buffer to be deallocated by the PULL thread
                //
                pPnr->fTemp   = TRUE;

//#if RPL_TYPE
                //
                // We need to pull according to the RplType for the pull pnrs
                //
                pPnr->RplType = WinsCnf.PullInfo.RplType;
//#endif

       }


        if (fRplPnr)
        {
            pPullRangeInfo = WinsMscHeapAlloc(
                                        NmsRpcHeapHdl,
                                        sizeof(WINSINTF_PULL_RANGE_INFO_T)
                                             );
#if 0
            WinsMscAlloc(sizeof(WINSINTF_PULL_RANGE_INFO_T),
                                  &pPullRangeInfo);
#endif
            pPullRangeInfo->pPnr      =  pPnr;
            pPullRangeInfo->OwnAdd    =  *pOwnerAdd;
            pPullRangeInfo->MinVersNo =  MinVersNo;
            pPullRangeInfo->MaxVersNo =  MaxVersNo;


           //
           // Call RplInsertQue to insert the push request to
           // the Pull Thread
           //
           ERplInsertQue(
                     WINS_E_WINSRPC,
                     QUE_E_CMD_PULL_RANGE,
                     NULL,        //no Dlg Hdl
                     NULL,        //no msg is there
                     0,                //msg length
                     pPullRangeInfo,       //client context
                     pPnr->MagicNo
                     );
        }
 }
except (EXCEPTION_EXECUTE_HANDLER) {
                DBGPRINTEXC("WinsPullRange");
                WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_PUSH_TRIGGER_EXC);
                RetCode = WINSINTF_FAILURE;
  }

        //
        // Leave the critical section guarded by NmsNmhNamRegCrtSec.
        //
        LeaveCriticalSection(&NmsNmhNamRegCrtSec);
        LeaveCriticalSection(&WinsCnfCnfCrtSec);

        //
        // if replication was allowed only with configured partners and
        // there was no WINS with the address specified by the client,
        // return failure
        //
        if (!fRplPnr)
        {
                RetCode = WINSINTF_FAILURE;
        }

        return(RetCode);
}

DWORD
WinsSetPriorityClass(
        IN WINSINTF_PRIORITY_CLASS_E        PriorityClass_e
        )

/*++

Routine Description:
        This function sets the priority class of the Wins process.

Arguments:
        PriorityClass -- Priority Class of the WINS process

Externals Used:
        WinsCnf


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/
{

        //DWORD   OldPrCls;
        DWORD   NewPrCls;
        HANDLE  ProcHdl;
        DWORD   RetVal = WINSINTF_SUCCESS;

        switch(PriorityClass_e)
        {
                case(WINSINTF_E_NORMAL):
                        NewPrCls = NORMAL_PRIORITY_CLASS;
                        break;
                case(WINSINTF_E_HIGH):
                        NewPrCls = HIGH_PRIORITY_CLASS;
                        break;
                default:
                        DBGPRINT0(DET, "WinsSetPriorityClass: Invalid Priority Class\n");
                        return(WINSINTF_FAILURE);
                        break;
        }


        ProcHdl = GetCurrentProcess();

        EnterCriticalSection(&WinsCnfCnfCrtSec);
#if 0
try {
FUTURES("Use a WinsMsc functions here for consistency")

        if ((OldPrCls = GetPriorityClass(ProcHdl)) == 0)
        {
                DBGPRINT1(ERR, "WinsSetPriorityClass: Can not Proc Priority. Error = (%d)\n", GetLastError());
                RetVal = WINSINTF_FAILURE;
        }
        else
        {
           if (OldPrCls == NewPrCls)
           {
                DBGPRINT1(ERR, "WinsSetPriorityClass: Process already has this Priority Class = (%d)\n", NewPrCls);
           }
           else
           {
#endif
                if (SetPriorityClass(ProcHdl, NewPrCls) == FALSE)
                {
                        DBGPRINT1(ERR, "WinsSetPriorityClass: SetPriorityClass() Failed. Error = (%d)\n", GetLastError());
                }
                else
                {
                        WinsCnf.WinsPriorityClass = (DWORD)PriorityClass_e;
                }
#if 0
           }
       }
  }
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsSetPriorityCls");
        }
#endif

        //
        // ProcHdl is a pseudo-handle and does not need to be closed
        //
        LeaveCriticalSection(&WinsCnfCnfCrtSec);
        return(WINSINTF_SUCCESS);
}

DWORD
WinsResetCounters(
        VOID
        )

/*++

Routine Description:
        This function resets/clears the counters

Arguments:
        None

Externals Used:
        NmsNmhNamRegCrtSec

Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:
        R_WinsResetCounters

Side Effects:

Comments:
        None
--*/

{
  DWORD i;
  PRPL_CONFIG_REC_T         pPnr;
  //
  // Copy the counters
  //
  EnterCriticalSection(&NmsNmhNamRegCrtSec);
  (VOID)RtlFillMemory(&WinsIntfStat.Counters, sizeof(WinsIntfStat.Counters), 0);
  LeaveCriticalSection(&NmsNmhNamRegCrtSec);
  // now clear the per partner info.
  EnterCriticalSection(&WinsCnfCnfCrtSec);
  pPnr       = WinsCnf.PullInfo.pPullCnfRecs;
  for (i = 0; (i<WinsCnf.PullInfo.NoOfPushPnrs) && (pPnr->WinsAdd.Add.IPAdd != INADDR_NONE) ; i++) {
      pPnr->NoOfRpls = 0;
      pPnr->NoOfCommFails = 0;
      pPnr = WinsCnfGetNextRplCnfRec(pPnr,RPL_E_IN_SEQ);
  }
  LeaveCriticalSection(&WinsCnfCnfCrtSec);

  //
  // Even if we have multiple threads doing resets (unlikely occurence),
  // the window between the above critical section and the one entered
  // by the following function does not cause any problem.
  //
  WinsIntfSetTime(NULL, WINSINTF_E_COUNTER_RESET);

  return(WINSINTF_SUCCESS);
}

DWORD
WinsWorkerThdUpd(
        DWORD NewNoOfNbtThds
        )

/*++

Routine Description:
        This function is called to change the count of the NBT threads in
        the WINS process.

Arguments:
        NewNoOfNbtThds  - The new count of the Nbt threads

Externals Used:
        None


Return Value:

   Success status codes --  WINSINTF_SUCCESS
   Error status codes   --  WINSINTF_FAILURE

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/
{

        //
        // If the WINS server is not in steady state or if the new count
        // of Nbt threads requested is outside the allowed range, return
        // failure
        //
CHECK("Somehow, if the number of threads is equal to the max. number allowed")
CHECK("pTls comes out as NULL for all NBT threads (seen at termination)")
          if (

             ((WinsCnf.State_e != WINSCNF_E_RUNNING)
                        &&
             (WinsCnf.State_e != WINSCNF_E_PAUSED))
                        ||
             (NewNoOfNbtThds >= WINSTHD_MAX_NO_NBT_THDS)
                        ||
             (NewNoOfNbtThds < WINSTHD_MIN_NO_NBT_THDS)
          )
          {
                return(WINSINTF_FAILURE);
          }

        EnterCriticalSection(&WinsCnfCnfCrtSec);
        WinsIntfNoOfNbtThds = NewNoOfNbtThds;

try {
        //
        // If the new count is more than the existing count, store the new
        // count in a global and signal an Nbt thread. The signaled
        // Nbt thread will create all the extra threads needed
        //
        if (NewNoOfNbtThds > NmsNoOfNbtThds)
        {
                WinsMscSignalHdl(NmsCrDelNbtThdEvt);
        }
        else
        {
                //
                // if the new count is same as the existing count, return
                // success
                //
                if (NewNoOfNbtThds == NmsNoOfNbtThds)
                {
                        DBGPRINT1(FLOW, "WinsWorkerThdUpd: Wins server already has %d threads\n", NewNoOfNbtThds);
                }
                else  // NewNoOfNbtThds < NmsNoOfNbtThds
                {
                        //
                        // Signal a thread to delete self. The signaled thread will
                        // signal the event again if more than one thread has to be
                        // deleted
                        //
                        WinsMscSignalHdl(NmsCrDelNbtThdEvt);
                }
        }
}
except (EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsWorkerThdUpd");
        }

        LeaveCriticalSection(&WinsCnfCnfCrtSec);

        return(WINSINTF_SUCCESS);
}



DWORD
WinsGetNameAndAdd(
        PWINSINTF_ADD_T   pWinsAdd,
        LPBYTE                      pUncName
        )
{
  DWORD RetStat = WINSINTF_SUCCESS;

//  TCHAR UncName[MAX_COMPUTERNAME_LENGTH + 1];
//  DWORD LenOfBuff = WINSINTF_MAX_COMPUTERNAME_LENGTH;
  DWORD LenOfBuff = MAX_COMPUTERNAME_LENGTH + 1;
  pWinsAdd->IPAdd = NmsLocalAdd.Add.IPAdd;
FUTURES("Change this to GetComputerName when winsadmn is made unicode compliant")
  if (GetComputerNameA(pUncName, &LenOfBuff) == FALSE)
  {
     DBGPRINT1(ERR, "WinsGetNameAndAdd: Name error. Error=(%x)\n", GetLastError());
     RetStat = GetLastError();
  }
  return(RetStat);
}



#define INITIAL_RAMPUP_NO       3

DWORD
WinsGetBrowserNames(
        PWINSINTF_BIND_DATA_T             pWinsHdl,
        PWINSINTF_BROWSER_NAMES_T         pNames
        )
{

        DWORD              RetVal = WINSINTF_SUCCESS;
        time_t             CurrentTime;
        static   DWORD     sNoOfTimes = 0;
        static   time_t    sLastTime = 0;
        BOOL               fPopCache = FALSE;


        UNREFERENCED_PARAMETER(pWinsHdl);

        //
        // If this the initial ramp up period, populate the cache
        //
        if (sNoOfTimes++ < INITIAL_RAMPUP_NO)
        {
                //
                // if this is the first call, create the dom. cache event.
                //
                if (sNoOfTimes == 1)
                {
                    WinsMscCreateEvt(L"WinsDomCachEvt", FALSE, &sDomCache.EvtHdl);
                }
                DBGPRINT1(SPEC, "WinsGetBrowserNames: sNoOfTimes = (%d)\n", sNoOfTimes);
                fPopCache = TRUE;
        }
        else
        {
          //
          // Initial ramp up period is past.  Populate the cache if 3 mts
          // have expired since it was last populated
          //
          if ((time(&CurrentTime) - sLastTime) > THREE_MTS || sDomCache.bRefresh)
          {
                DBGPRINT0(SPEC, "WinsGetBrowserNames: Pop Cache due to timeout\n");
                sDomCache.bRefresh = FALSE;
                sLastTime = CurrentTime;
                fPopCache = TRUE;
          }
        }
try {
        //
        // Populate the cache if fPopCache is set or if the number of entries
        // in the current cache are 0
        //
        if (fPopCache || (sDomCache.SzOfBlock == 0))
        {
          //
          // if our cache has some data, deallocate it first.
          //
          // Note: There could be an rpc thread in the rpc code accessing
          // this buffer. I can't free this buffer until it is done.
          //
          if (sDomCache.SzOfBlock > 0)
          {
                DWORD i;
                PWINSINTF_BROWSER_INFO_T pBrInfo = sDomCache.pInfo;
                DWORD NoOfUsers;

                //
                // Wait until all users are done. We won't iterate more than
                //
                // We can iterate a max. of INITIAL_RAMPUP_NO of times and
                // that too only at initial ramp up time. If a thread is
                // waiting on the event, another thread will also wait
                // on it (except during initial rampup time)
                //
                do {
                 EnterCriticalSection(&WinsIntfNoOfUsersCrtSec);
                 NoOfUsers = sDomCache.NoOfUsers;
                 LeaveCriticalSection(&WinsIntfNoOfUsersCrtSec);
                 if (NoOfUsers > 0)
                 {
                    WinsMscWaitInfinite(sDomCache.EvtHdl);
                 }
                } while (NoOfUsers > 0);
                //
                // Free all memory allocated for names
                //
                for (i=0;  i< sDomCache.EntriesRead; i++, pBrInfo++)
                {
                   midl_user_free(pBrInfo->pName);
                }

                //
                // Free the main block
                //
                midl_user_free(sDomCache.pInfo);
                sDomCache.SzOfBlock = 0;
                pNames->EntriesRead = 0;
                pNames->pInfo = NULL;
          }

          NmsDbThdInit(WINS_E_WINSRPC);
          NmsDbOpenTables(WINS_E_WINSRPC);
          DBGMYNAME("RPC-WinsGetBrowserNames");

          //
          // Get all records starting with 1B Names
          //
          RetVal = NmsDbGetNamesWPrefixChar(
                                        0x1B,
                                        &pNames->pInfo,
                                        &pNames->EntriesRead
                                          );
          NmsDbCloseTables();
          NmsDbEndSession();

          //
          // Store the info. only if there is something to be stored.
          //
          if (
                (RetVal == WINS_SUCCESS)
                        &&
                (pNames->EntriesRead > 0)
             )
          {
             sDomCache.SzOfBlock =
                        pNames->EntriesRead * sizeof(WINSINTF_BROWSER_INFO_T);
            // sDomCache.pInfo = midl_user_allocate(sDomCache.SzOfBlock);
            // WINSMSC_COPY_MEMORY_M(sDomCache.pInfo, pNames->pInfo,
    //                                            sDomCache.SzOfBlock);
             sDomCache.pInfo = pNames->pInfo;
             sDomCache.EntriesRead = pNames->EntriesRead;
          }
          else
          {
                //
                // We did not get anything from the db
                //
                sDomCache.SzOfBlock = 0;
                pNames->EntriesRead = 0;
                pNames->pInfo = NULL;
          }
        }
        else
        {
                //
                // Use the cached info.
                //
                //pNames->pInfo = midl_user_allocate(sDomCache.SzOfBlock);
                //WINSMSC_COPY_MEMORY_M(pNames->pInfo, sDomCache.pInfo,
                //                                sDomCache.SzOfBlock);
                pNames->pInfo = sDomCache.pInfo;
                pNames->EntriesRead = sDomCache.EntriesRead;
        }
 }
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsGetBrowserNames");
        WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_BROWSER_NAME_EXC);
        pNames->EntriesRead = 0;
        pNames->pInfo = NULL;
        RetVal = WINSINTF_FAILURE;
        }
        return(RetVal);
}

VOID
R_WinsGetBrowserNames_notify_flag(boolean __MIDL_NotifyFlag
)
/*++

Routine Description:
  Called by rpc to indicate that it is done with the buffer returned by
  WinsGetBrowserNames

Arguments:

Externals Used:
	None

	
Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
	None
--*/

{
     //
     // Decrement the user count. If equal to 0, signal the event to let
     // another thread go on.
     //
     EnterCriticalSection(&WinsIntfNoOfUsersCrtSec);

     //
     // workaround an rpc bug (18627) where it may call notify without calling
     // R_WinsGetBrowserNames (checkout winsif_s.c)
     //
     if (
           (sDomCache.NoOfUsers > 0) &&
           (--sDomCache.NoOfUsers == 0) &&
           sDomCache.EvtHdl != NULL
        )
     {
          WinsMscSignalHdl(sDomCache.EvtHdl);
     }
     LeaveCriticalSection(&WinsIntfNoOfUsersCrtSec);
     return;
}

DWORD
WinsDeleteWins(
        PWINSINTF_ADD_T   pWinsAdd
        )
{
        PCOMM_ADD_T        pAdd;
        DWORD                RetVal = WINSINTF_FAILURE;


        if (pWinsAdd->IPAdd == NmsLocalAdd.Add.IPAdd)
        {
                WINSINTF_VERS_NO_T MinVersNo = {0};
                WINSINTF_VERS_NO_T MaxVersNo = {0};
                RetVal = WinsDelDbRecs(pWinsAdd, MinVersNo, MaxVersNo);
#if 0
                //
                // We always keep the entry for the local WINS. For any
                //
                DBGPRINT0(ERR, "WinsDeleteWins: Sorry, you can not delete the entry for the local WINS\n");
                WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_DELETE_LOCAL_WINS_DISALLOWED);
                RetVal = WINSINTF_CAN_NOT_DEL_LOCAL_WINS;
#endif
        }
        else
        {
                WCHAR String[WINS_MAX_NAME_SZ];
                struct in_addr InAddr;

                InAddr.s_addr = htonl(pWinsAdd->IPAdd);
                (VOID)WinsMscConvertAsciiStringToUnicode(
                            inet_ntoa( InAddr),
                            (LPBYTE)String,
                            WINS_MAX_NAME_SZ);

                WinsLogAdminEvent(WINS_EVT_ADMIN_DEL_OWNER_INITIATED,1,String);

                //
                // Allocate from the general heap (not from the rpc heap)
                // since this memory will be deallocated by DeleteWins in
                // rplpull.c which I don't want to tie to just rpc work.
                //
                   WinsMscAlloc(sizeof(COMM_ADD_T), &pAdd);
                   pAdd->AddTyp_e = pWinsAdd->Type;
                   pAdd->AddLen    = pWinsAdd->Len;
                pAdd->Add.IPAdd = pWinsAdd->IPAdd;

                //
                // Call RplInsertQue to insert the push request to
                // the Pull Thread
                //
                ERplInsertQue(
                     WINS_E_WINSRPC,
                     QUE_E_CMD_DELETE_WINS,
                     NULL,        //no Dlg Hdl
                     NULL,        //no msg is there
                     0,                //msg length
                     pAdd,   //client context,
                     0      //no magic no
                     );
                RetVal = WINSINTF_SUCCESS;
        }
        return(RetVal);
}

#define MAX_RECS_TO_RETURN  5000
DWORD
WinsGetDbRecsByName (
        PWINSINTF_ADD_T             pWinsAdd,
        DWORD                       Location,
        LPBYTE                      pName,
        DWORD                       NameLen,
        DWORD                       NoOfRecsDesired,
        DWORD                       TypeOfRecs,
        PWINSINTF_RECS_T            pRecs
        )

/*++

Routine Description:
        This function returns with all the records (that can fit into the
        buffer passed) owned by a WINS in the local db of this WINS.

Arguments:


Externals Used:
        None


Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
        None
--*/

{
        COMM_ADD_T           Address;
        LPVOID               pBuff = NULL;
        DWORD                BuffLen;
        DWORD                NoOfRecs = 0;
        DWORD                Status;
        PWINSTHD_TLS_T        pTls;

        DBGENTER("WinsGetDbRecsByName\n");


        if ((NoOfRecsDesired == 0) || (NoOfRecsDesired > MAX_RECS_TO_RETURN))
        {
                NoOfRecsDesired = MAX_RECS_TO_RETURN;
        }


        if ((pWinsAdd != NULL) && (pWinsAdd->IPAdd != 0))
        {
                  Address.AddTyp_e  = pWinsAdd->Type;
                  Address.AddLen    = pWinsAdd->Len;
                  Address.Add.IPAdd = pWinsAdd->IPAdd;
        }
        //
        // initialize this thread with the db engine
        //
        NmsDbThdInit(WINS_E_WINSRPC);
        NmsDbOpenTables(WINS_E_WINSRPC);
        DBGMYNAME("RPC-WinsGetDbRecsByName");
 try {
        if ((pName != NULL) && (NameLen != 0))
        {
           //
           // Terminate name with NULL, just in case user didn't do it.
           //
           *(pName + NameLen) = (BYTE)NULL;
        }
        if ((pName == NULL) && (NameLen > 0))
        {
             NameLen = 0;
        }

PERF("The caller can pass the number of records for which space has been")
PERF("allocated in buffer pointed to by pRec in the NoOfRecs field. We should")
PERF("We should pass this argument to NmsDbGetDataRecs so that it does not get")
PERF("more records than are necessary")

        Status = NmsDbGetDataRecsByName(
                        pName,
                        NameLen != 0 ? NameLen + 1 : 0,
                        Location,
                        NoOfRecsDesired,
                        pWinsAdd != NULL ? &Address : NULL,
                        TypeOfRecs,
                        &pBuff,
                        &BuffLen,
                        &NoOfRecs
                        );


        if (Status == WINS_SUCCESS)
        {
            Status = PackageRecs( pBuff, BuffLen, NoOfRecs, pRecs);
        }

      }
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsGetDbRecsByName");
        WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_RPC_EXC);
        Status = WINS_FAILURE;
 }
        //
        // Free the buffer and destroy the heap.
        //
        GET_TLS_M(pTls);
        if (pTls->HeapHdl != NULL)
        {
               if (pBuff != NULL)
               {
                  WinsMscHeapFree(pTls->HeapHdl, pBuff);
               }
               WinsMscHeapDestroy(pTls->HeapHdl);
//               pTls->HeapHdl = NULL;
        }

        //
        // Let us end the session
        //
        NmsDbCloseTables();
        NmsDbEndSession();

        if (Status != WINS_SUCCESS)
        {
            pRecs->pRow = NULL;
            pRecs->NoOfRecs = 0;
            Status = WINSINTF_FAILURE;
        }
        else
        {
            if (pRecs->NoOfRecs == 0)
            {
              pRecs->pRow = NULL;
              pRecs->NoOfRecs = 0;
              Status = WINSINTF_REC_NOT_FOUND;
            }
        }
        DBGLEAVE("WinsGetDbRecsByName\n");
        return (Status);
}

STATUS
PackageRecs(
        PRPL_REC_ENTRY2_T     pBuff,
        DWORD                BuffLen,
        DWORD                NoOfRecs,
        PWINSINTF_RECS_T     pRecs
     )

/*++

Routine Description:


Arguments:


Externals Used:
	None

	
Return Value:

   Success status codes --
   Error status codes   --

Error Handling:

Called by:

Side Effects:

Comments:
	None
--*/

{
//        ULONG                Status;
        BOOL                 fExcRaised = FALSE;
        PWINSINTF_RECORD_ACTION_T pRow;
        DWORD                i;
        DWORD                ind;
        DWORD                EntType;
 //       DWORD                MaxAdds;
        PWINSTHD_TLS_T       pTls;

        DBGENTER("PackageRecs\n");

        i = 0;
        GET_TLS_M(pTls);
try {

        //
        // If there are records to send back and the client has specified
        // a buffer for them, insert the records
        //
        if  (NoOfRecs > 0)
        {

          //
          // Allocate memory for the no of records
          //
          pRecs->BuffSize  =  sizeof(WINSINTF_RECORD_ACTION_T) * NoOfRecs;

          //
          // If memory can not be allocate, an exception will be returned
          // by midl_user_alloc
          //
          pRecs->pRow      =  midl_user_allocate(pRecs->BuffSize);

#if 0
          pRecs->pRow      =  RpcSmAllocate(pRecs->BuffSize, &Status);

          if (Status != RPC_S_OK)
          {
             DBGPRINT1(ERR, "PackageRecs: RpcSmAllocate returned error = (%x)\n", Status);
          }
#endif
          pRow                    =  pRecs->pRow;


          for (; i<NoOfRecs; i++)
          {

                //
                // Initialize so that we don't get "enum wrong" error.
                //
                pRow->Cmd_e = WINSINTF_E_QUERY;

                //
                // the name retrieved has NULL as the last character.  This
                // We need to pass a name without this NULL.
                //
                pRow->NameLen = pBuff->NameLen;
                if (*pBuff->pName == 0x1B)
                {
                        WINS_SWAP_BYTES_M(pBuff->pName, pBuff->pName + 15);
                }

                // +1 added to fix #390830
                pRow->pName =  midl_user_allocate(pRow->NameLen + 1);
#if 0
                pRow->pName =  RpcSmAllocate(pRow->NameLen, &Status);
                if (Status != RPC_S_OK)
                {
                   DBGPRINT1(ERR, "PackageRecs: RpcSmAllocate returned error = (%x)\n", Status);
                }
#endif
                WINSMSC_COPY_MEMORY_M(pRow->pName, pBuff->pName,pRow->NameLen);
                WinsMscHeapFree(pTls->HeapHdl, pBuff->pName);

                EntType = NMSDB_ENTRY_TYPE_M(pBuff->Flag);
                pRow->TypOfRec_e = NMSDB_ENTRY_UNIQUE_M(EntType)
                                                         ? WINSINTF_E_UNIQUE :
                                (NMSDB_ENTRY_NORM_GRP_M(EntType) ?
                                        WINSINTF_E_NORM_GROUP :
                                (NMSDB_ENTRY_SPEC_GRP_M(EntType) ?
                                        WINSINTF_E_SPEC_GROUP :
                                        WINSINTF_E_MULTIHOMED));


                if (
                    (pRow->TypOfRec_e == WINSINTF_E_SPEC_GROUP) ||
                    (pRow->TypOfRec_e == WINSINTF_E_MULTIHOMED)
                   )
                {
                        PWINSINTF_ADD_T       pAdd;
                        DWORD                 No;

                        if (pBuff->NoOfAdds > 0)
                        {
                            pRow->NoOfAdds = pBuff->NoOfAdds * 2;


                           //
                           // Each member is comprised of two addresses,
                           // first address is that of the owner WINS, second
                           // address is that of the node registered
                           //
                           pRow->pAdd        =
//                             RpcSmAllocate(
                             midl_user_allocate(
                                        (unsigned int)(pRow->NoOfAdds)
                                                *
                                        sizeof(WINSINTF_ADD_T)//,
//                                        &Status
                                             );

#if 0
          if (Status != RPC_S_OK)
          {
             DBGPRINT1(ERR, "WinsGetDbRecs: RpcSmAllocate returned error = (%x)\n", Status);
          }
#endif

                           for (
                                No= 0, ind= 0, pAdd = pRow->pAdd;
                                No < (pRow->NoOfAdds/2);
                                No++
                            )
                          {
                           pAdd->Type     =  (UCHAR)(pBuff->pNodeAdd + ind)->AddTyp_e;
                           pAdd->Len      =  (pBuff->pNodeAdd + ind)->AddLen;
                           pAdd++->IPAdd  =  (pBuff->pNodeAdd + ind)->Add.IPAdd;

                           pAdd->Type     =  (UCHAR)(pBuff->pNodeAdd + ++ind)->AddTyp_e;
                           pAdd->Len      =  (pBuff->pNodeAdd + ind)->AddLen;
                           pAdd++->IPAdd  =  (pBuff->pNodeAdd + ind++)->Add.IPAdd;
                          }
                          WinsMscHeapFree(pTls->HeapHdl, pBuff->pNodeAdd);
                       }
                }
                else
                {
                          pRow->NoOfAdds =  0;
                          pRow->pAdd       = NULL;
                          pRow->Add.Type   = (UCHAR)pBuff->NodeAdd[0].AddTyp_e;
                          pRow->Add.Len    = pBuff->NodeAdd[0].AddLen;
                          pRow->Add.IPAdd  = pBuff->NodeAdd[0].Add.IPAdd;
                }
                pRow->NodeTyp     = (BYTE)NMSDB_NODE_TYPE_M(pBuff->Flag);
                pRow->fStatic     = NMSDB_IS_ENTRY_STATIC_M(pBuff->Flag);
                pRow->State_e     = NMSDB_ENTRY_STATE_M(pBuff->Flag);
                pRow->VersNo      = pBuff->VersNo;
                pRow->TimeStamp   = pBuff->TimeStamp;
                pRow->OwnerId     = pBuff->OwnerId;
                pRow++;


                pBuff = (PRPL_REC_ENTRY2_T)((LPBYTE)pBuff + RPL_REC_ENTRY2_SIZE);
PERF("Do the addition above the for loop and store in a var. Use var. here")

         } // end of for loop
        } //end of if block
        else
        {
                pRecs->pRow = NULL;
        }

 }        // end of try
except(EXCEPTION_EXECUTE_HANDLER) {
        DBGPRINTEXC("WinsGetDbRecs");
        WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_RPC_EXC);
        fExcRaised = TRUE;
        }

        pRecs->TotalNoOfRecs = NoOfRecs;

        if (!fExcRaised)
        {
           pRecs->NoOfRecs = i;
        }
        else
        {
        //     RpcSmFree(pRecs->pRow);
            midl_user_free(pRecs->pRow);
            pRecs->NoOfRecs = 0;
        }

        DBGENTER("PackageRecs\n");
        return (WINSINTF_SUCCESS);
}




//void __RPC_FAR * __RPC_API
void *
midl_user_allocate(size_t cBytes)
{
#if 0
//#ifdef WINSDBG
        LPVOID pMem = WinsMscHeapAlloc(NmsRpcHeapHdl, cBytes);
        DBGPRINT1(DET, "midl_user_alloc: Memory allocated is (%d)\n", pMem);
        return(pMem);
//#else
#endif
        return(WinsMscHeapAlloc(NmsRpcHeapHdl, cBytes));
}

//void __RPC_FAR __RPC_API
void
//midl_user_free(void __RPC_FAR *pMem)
midl_user_free(void  *pMem)
{
        if (pMem != NULL)
        {
//                DBGPRINT1(DET, "midl_user_free: Memory to free is (%d)\n", pMem);
                WinsMscHeapFree(NmsRpcHeapHdl, pMem);
        }
        return;
}

VOID
LogClientInfo(
  RPC_BINDING_HANDLE ClientHdl,
  BOOL                   fAbruptTerm
  )
{
  RPC_STATUS        RpcRet;
  RPC_BINDING_HANDLE Binding;
  PTUCHAR pStringBinding;
  PTUCHAR pProtSeq;
  PTUCHAR pNetworkAddress;
  WINSEVT_STRS_T EvtStrs;

NOTE("remove #if 0 when we go to 540 or above")
#if 0
  RpcRet = RpcBindingServerFromClient(ClientHdl,  &Binding);

  if (RpcRet != RPC_S_OK)
  {
        DBGPRINT1(ERR, "LogClientInfo: Can not get binding handle. Rpc Error = (%d)\nThis could be because named pipe protocol is being used\n", RpcRet);
        Binding = ClientHdl;
  }
#endif
NOTE("remove when we go to 540 or above")
  Binding = ClientHdl;


  RpcRet = RpcBindingToStringBinding(Binding, &pStringBinding);
  if (RpcRet != RPC_S_OK)
  {
        DBGPRINT1(ERR, "LogClientInfo: RpcBindingToStringBinding returned error = (%d)\n", RpcRet);
          return;
  }
  RpcRet = RpcStringBindingParse(
                                pStringBinding,
                                NULL,        //don't want uuid
                                &pProtSeq,
                                &pNetworkAddress,
                                NULL,                //end point
                                NULL                //network options
                                );
  if (RpcRet != RPC_S_OK)
  {
        DBGPRINT1(ERR, "LogClientInfo: RpcStringBindingParse returned error = (%d)\n", RpcRet);
        RpcStringFree(&pStringBinding);
          return;
  }

#ifndef UNICODE
  DBGPRINT2(FLOW, "LogClientInfo: The protocol sequence and address used by client are (%s) and (%s)\n", pProtSeq, pNetworkAddress);
#else
#ifdef WINSDBG
  IF_DBG(FLOW)
  {
    wprintf(L"LogClientInfo: The protocol sequence and address used by client are (%s) and (%s)\n", pProtSeq, pNetworkAddress);
  }
#endif
#endif
  RpcStringFree(&pProtSeq);
  RpcStringFree(&pNetworkAddress);

  EvtStrs.NoOfStrs = 1;
  EvtStrs.pStr[0] = (LPTSTR)pNetworkAddress;
  if (fAbruptTerm)
  {
    WINSEVT_LOG_STR_D_M(WINS_EVT_ADMIN_ABRUPT_SHUTDOWN, &EvtStrs);
  }
  else
  {
    WINSEVT_LOG_STR_D_M(WINS_EVT_ADMIN_ORDERLY_SHUTDOWN, &EvtStrs);
  }
  return;
}