text: //+-------------------------------------------------------------------------
  //
  //  Microsoft Windows
  //
  //  Copyright (C) Microsoft Corporation, 1998 - 1999
  //
  //  File:       dnsutil.cpp
  //
  //--------------------------------------------------------------------------
  
  
  
  #include "preDNSsn.h"
  #include <SnapBase.h>
  
  #include "resource.h"
  #include "dnsutil.h"
  #include "uiutil.h"
  
  #ifdef DEBUG_ALLOCATOR
      #ifdef _DEBUG
      #define new DEBUG_NEW
      #undef THIS_FILE
      static char THIS_FILE[] = __FILE__;
      #endif
  #endif
  
  // formatting of IPv4 address to string
  
  LPCWSTR g_szIpStringFmt = TEXT("%d.%d.%d.%d");
  
  #ifdef NTRAID_628931
  // Wide character version of DNS_ZONE_ROOT_HINTS
  
  CString g_zoneRootHints(L"");
  
  HRESULT GetWideCharZoneRootHints(CString& zoneRootHints)
  {
      HRESULT hr = S_OK;
  
      // Initialize the wide char version if it has
      // not already been initialized.
      if (g_zoneRootHints == L"")
      {
          // Determine how long wide string needs to be.
          int wideLength = MultiByteToWideChar(
              CP_ACP,
              0,
              DNS_ZONE_ROOT_HINTS,
              -1, // Let MBtoWC() determine length.
              NULL,
              0);
  
          LPWSTR lpszZoneRootHints = new WCHAR[wideLength];
          if (lpszZoneRootHints)
          {
              // Convert ascii to wide string.
              int convertedLength = MultiByteToWideChar(
                  CP_ACP,
                  0,
                  DNS_ZONE_ROOT_HINTS,
                  -1,
                  lpszZoneRootHints,
                  wideLength);
  
              // Should never happen.
              if (convertedLength != wideLength)
              {
                  hr = E_FAIL;
                  ASSERT(false);
              }
  
              g_zoneRootHints = lpszZoneRootHints;
          }
          else
          {
              hr = E_OUTOFMEMORY;
          }
  
          delete [] lpszZoneRootHints;
      }
  
      if (SUCCEEDED(hr))
      {
          zoneRootHints = g_zoneRootHints;
      }
      return hr;
  }
  #endif //NTRAID_628931
  
  
  ///////////////////////////////////////////////////////////////
  // General Purpose Utility Functions
  
  BYTE HexCharToByte(WCHAR ch)
  {
      if (ch >= TEXT('0') && ch <= TEXT('9'))
          return static_cast<BYTE>(ch-TEXT('0'));
      else if (ch >= TEXT('A') && ch <= TEXT('F'))
          return static_cast<BYTE>(ch-TEXT('A') + 10);
      else if (ch >= TEXT('a') && ch <= TEXT('f')) 
          return static_cast<BYTE>(ch-TEXT('a') + 10);
    else
      return static_cast<BYTE>(0xFF); // marks out of range, expect 0x00 to 0x0f
  }
  
  
  void ReverseString(LPWSTR p, LPWSTR q)
  {
      WCHAR c;
      while (p < q)
      {
          c = *p;
          *p = *q;
          *q = c;
          p++; q--;
      }
  }
  
  int ReverseIPString(LPWSTR lpsz)
  {
      if (!lpsz)
          return 0;
      // reverse the whole string
      size_t nLen = wcslen(lpsz);
      ReverseString(lpsz, lpsz+(nLen-1));
  
      // reverse each octect
      WCHAR *p,*q1,*q2;
      p = q1 = q2 = lpsz;
      int nOctects = 0;
      while (TRUE)
      {
          if ( (*p == '.') || (*p == '\0') && (p >lpsz) )
          {
              q1 = p-1; // point to the digit before the dot
              ReverseString(q2,q1);
              nOctects++;
              q2 = p+1; // for next loop, set trailing pointer
          }
          if (!*p)
              break;
          p++;
      }
      return nOctects;
  }
  
  
  BOOL IsValidIPString(LPCWSTR lpsz)
  {
      return IPStringToAddr(lpsz) != INADDR_NONE;
  }
  
  
  DWORD IPStringToAddr(LPCWSTR lpsz)
  {
      USES_CONVERSION;
      DWORD dw =  inet_addr(W2A(lpsz));
      return dw;
  }
  
  
  
  
  /*
  
  #define MAX_OCTECT_DIGITS (3) // IPv4 only
  
  BOOL IsValidIPString(LPCWSTR lpsz)
  {
      if (!lpsz)
          return FALSE; // null
      int nLen = wcslen(lpsz);
      if (nLen <= 0)
          return FALSE; // empty
      if ((lpsz[0] == TEXT('.')) || (lpsz[nLen-1] == TEXT('.')) )
          return FALSE; // leading and trailing dots
      for (int k=0; k<nLen; k++)
          if ((lpsz[k] != TEXT('.')) && !isdigit(lpsz[k]))
              return FALSE; // wrong characters
  
      // look for octects and dots
      WCHAR *p,*q1,*q2;
      p = q1 = q2 = (WCHAR*)lpsz;
      while (TRUE)
      {
          if ( (*p == TEXT('.')) || (*p == TEXT('\0')) && (p >lpsz) )
          {
              q1 = p-1; // point to the digit before the dot
              if ((q1-q2)+1 > MAX_OCTECT_DIGITS)
                  return FALSE; // too many digits
              q2 = p+1; // for next loop, set trailing pointer
          }
          if (!*p)
              break; 
          p++;
      }
      return TRUE; // got at the end fine
  }
  
  */
  
  BOOL RemoveInAddrArpaSuffix(LPWSTR lpsz)
  {
      if (!lpsz)
          return FALSE;
      // assume NULL terminated string
      size_t nSuffixLen = wcslen(INADDR_ARPA_SUFFIX);
      size_t nLen = wcslen(lpsz);
      // first char in the suffix, if present
      WCHAR* p = lpsz + nLen - nSuffixLen; 
      if ((p < lpsz) || (_wcsicmp(p,INADDR_ARPA_SUFFIX) != 0)) 
          return FALSE; // string too short or not matching suffix
      // got the match, trim the suffix
      ASSERT(*p == L'.');
      *p = NULL;
      return TRUE;
  }
  
  DNS_STATUS ValidateDnsNameAgainstServerFlags(LPCWSTR lpszName, 
                                               DNS_NAME_FORMAT format, 
                                               DWORD serverNameChecking)
  {
    DNS_STATUS errName = ::DnsValidateName_W(lpszName, format);
  
    if (errName == ERROR_INVALID_NAME)
    {
      //
      // Always fail for invalid names
      // Invalid names are:
      //    - Longer than 255 characters
      //    - contains label longer than 63 characters
      //    - contains a space
      //    - contains two or more consecutive dots
      //    - begins with a dot
      //    - contains a dot if the name is submitted with format DnsNameHostDomainLabel or DnsNameHostNameLabel
      //
      return errName;
    }
  
    if (errName == DNS_ERROR_INVALID_NAME_CHAR)
    {
      if (serverNameChecking == DNS_ALLOW_MULTIBYTE_NAMES ||
          serverNameChecking == DNS_ALLOW_ALL_NAMES)
      {
        //
        // If server is set to allow UTF8 or all names let it pass
        //
        return 0;
      }
      else
      {
        //
        // If server is set to Strict RFC or non-RFC fail
        // DNS_ERROR_INVALID_NAME_CHAR will result from the following:
        //    - Contains any of the following invalid characters:  {|}~[\]^':;<=>?@!"#$%`()+/,
        //    - contains an asterisk (*) unless the asterisk is the first label in the multi-labeled name
        //      and submitted with format DnsNameWildcard
        //
        return errName;
      }
    }
  
    if (errName == DNS_ERROR_NUMERIC_NAME)
    {
      //
      // Always allow numeric names
      //
      return 0;
    }
  
    if (errName == DNS_ERROR_NON_RFC_NAME)
    {
      if (serverNameChecking == DNS_ALLOW_RFC_NAMES_ONLY)
      {
        //
        // Fail if the server is only allowing strict RFC names
        // DNS_ERROR_NON_RFC_NAME will result from the following:
        //    - Contains at least one extended or Unicode character
        //    - contains underscore (_) unless the underscore is the first character in a label
        //      in the name submitted with format set to DnsNameSrvRecord
        //
        return errName;
      }
      else
      {
        //
        // Allow the name for any other server settings
        //
        return 0;
      }
    }
  
    return errName;
  }
  
  
  BOOL _HasSuffixAtTheEnd(LPCWSTR lpsz, int nLen, LPCWSTR lpszSuffix)
  {
      if (!lpsz)
          return FALSE; // was NULL
      // assume NULL terminated string
      size_t nSuffixLen = wcslen(lpszSuffix);
      // first char in the suffix, if present
      WCHAR* p = (WCHAR*)(lpsz + nLen - nSuffixLen);
      if (p < lpsz)
          return FALSE; // string too short
      if (_wcsicmp(p,lpszSuffix) != 0)
          return FALSE; // not matching suffix
  
      if (p == lpsz)
          return TRUE; // exactly matching
  
      // the suffix can be matching, but as part of a label
      if (p[-1] == TEXT('.'))
          return TRUE;
  
      return FALSE;
  }
  
  
  BOOL _IsValidDnsFwdLookupZoneName(CString& szName)
  {
      int nLen = szName.GetLength();
  
      // this is the "." (root zone)
      if ( nLen == 1 && (szName[0] == TEXT('.')) )
          return TRUE;
  
      // no dots at the beginning of the name
      if (szName[0] == TEXT('.'))
          return FALSE;
  
      // we can allow only one dot at the end
      if ( nLen >=2 && szName[nLen-1] == TEXT('.')  && szName[nLen-2] == TEXT('.') )
      {
          return FALSE;
      }
  
      // do not allow repeated dots
      for (int k=1; k < nLen; k++)
          if ( (szName[k] == TEXT('.')) && (szName[k-1] == TEXT('.')) )
              return FALSE;
  
      if (_HasSuffixAtTheEnd(szName, nLen, _T("ipv6.int")) || 
          _HasSuffixAtTheEnd(szName, nLen, _T("ipv6.int.")) ||
          _HasSuffixAtTheEnd(szName, nLen, _T("arpa")) ||
          _HasSuffixAtTheEnd(szName, nLen, _T("arpa.")) ||
          _HasSuffixAtTheEnd(szName, nLen, _T("ip6.int")) ||
          _HasSuffixAtTheEnd(szName, nLen, _T("ip6.int.")))
          return FALSE;
      return TRUE;
  }
  
  BOOL _IsValidDnsRevLookupZoneName(CString& szName)
  {
      int nLen = szName.GetLength();
      // do not allow dots at the beginning
      if (szName[0] == TEXT('.'))
      {
          return FALSE;
      }
  
      // do not allow repeated dots
      for (int k=1; k < nLen; k++)
      {
          if ( (szName[k] == TEXT('.')) && (szName[k-1] == TEXT('.')) )
          {
              return FALSE;
          }
      }
  
      if (!_HasSuffixAtTheEnd(szName, nLen, _T("ipv6.int")) &&
          !_HasSuffixAtTheEnd(szName, nLen, _T("ipv6.int.")) &&
          !_HasSuffixAtTheEnd(szName, nLen, _T("arpa")) &&
          !_HasSuffixAtTheEnd(szName, nLen, _T("arpa.")) &&
          !_HasSuffixAtTheEnd(szName, nLen, _T("ip6.int")) &&
          !_HasSuffixAtTheEnd(szName, nLen, _T("ip6.int.")))
      {
          return FALSE;
      }
  
      // Do not allow our question mark prefix.  Should have been removed.
      if (szName.Find(QUESTION_MARK_PREFIX) != -1)
      {
          return FALSE;
      }
  
      return TRUE;
  }
  
  /*
  BOOL _IsValidDnsRevLookupZoneName(CString& szName)
  {
      int nLen = szName.GetLength();
      // do not allow dots at the end or at the beginning
      if ( (szName[nLen-1] == TEXT('.')) || (szName[0] == TEXT('.')) )
          return FALSE;
  
      // do not allow repeated dots
      for (int k=1; k < nLen; k++)
          if ( (szName[k] == TEXT('.')) && (szName[k-1] == TEXT('.')) )
              return FALSE;
  
      if (!_HasSuffixAtTheEnd(szName, nLen, _T("ip6.int")) && 
          !_HasSuffixAtTheEnd(szName, nLen, _T("arpa")))
          return FALSE;
  
      return TRUE;
  }
  */
  
  BOOL IsValidDnsZoneName(CString& szName, BOOL bFwd)
  {
      // check for length
      int nLen = UTF8StringLen(szName);
      if ( (nLen <= 0) || (nLen > MAX_DNS_NAME_LEN))
          return FALSE;
  
      // do not allow blanks inside the zone name
      if (szName.Find(TEXT(' ')) != -1)
          return FALSE;
  
      return bFwd ? _IsValidDnsFwdLookupZoneName(szName) :
                  _IsValidDnsRevLookupZoneName(szName);
  }
  
  
  ///////////////////////////////////////////////////////////////
  // helper functions for IPv6 format
  
  void FormatIPv6Addr(CString& szAddr, IPV6_ADDRESS* ipv6Addr)
  {
      szAddr.Format(_T("%.4x:%.4x:%.4x:%.4x:%.4x:%.4x:%.4x:%.4x"),
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[0]),
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[1]), 
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[2]), 
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[3]), 
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[4]), 
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[5]), 
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[6]), 
          REVERSE_WORD_BYTES(ipv6Addr->IP6Word[7])    );
  }
  
  
  
  
  //////////////////////////////////////////////////////////////////////////////
  // CDNSServerInfoEx
  
  extern LPCSTR _DnsServerRegkeyStringArr[] = {
      DNS_REGKEY_NO_RECURSION,
      DNS_REGKEY_BIND_SECONDARIES,
      DNS_REGKEY_STRICT_FILE_PARSING,
      DNS_REGKEY_ROUND_ROBIN,
      DNS_REGKEY_LOCAL_NET_PRIORITY,
      DNS_REGKEY_SECURE_RESPONSES,
  };
  
  
  CDNSServerInfoEx::CDNSServerInfoEx()
  {
      m_pServInfo = NULL;
      m_errServInfo = 0;
  }
  
  CDNSServerInfoEx::~CDNSServerInfoEx()
  {
      FreeInfo();
  }
  
  DNS_STATUS CDNSServerInfoEx::Query(LPCTSTR lpszServerName)
  {
      DNS_RPC_SERVER_INFO* pServerInfo = NULL;
  
      // update original struct
      m_errServInfo = ::DnssrvGetServerInfo(lpszServerName, &pServerInfo);
      if (m_errServInfo != 0)
      {
          if (pServerInfo != NULL)
              ::DnssrvFreeServerInfo(pServerInfo);
          return m_errServInfo;
      }
      ASSERT(pServerInfo != NULL); 
      FreeInfo();
      m_pServInfo = pServerInfo;
  
      // if we succeeded and it is an NT 4.0 server, change the version info
      if (m_pServInfo->dwVersion == 0)
      {
          m_pServInfo->dwVersion = DNS_SRV_VERSION_NT_4;
      }
  
      return m_errServInfo;
  }
  
  void CDNSServerInfoEx::FreeInfo()
  {
      if (m_pServInfo != NULL)
      {
          ::DnssrvFreeServerInfo(m_pServInfo);
          m_pServInfo = NULL;
      }
      m_errServInfo = 0;
  }
  
  
  //////////////////////////////////////////////////////////////////////////////
  // CDNSZoneInfoEx
  CDNSZoneInfoEx::CDNSZoneInfoEx()
  {
      m_pZoneInfo = NULL;
  //  m_nAllowsDynamicUpdate = ZONE_UPDATE_OFF;
      m_errZoneInfo = 0;
  //  m_errAllowsDynamicUpdate = 0;
  }
  
  CDNSZoneInfoEx::~CDNSZoneInfoEx()
  {
      FreeInfo();
  }
  
  DNS_STATUS CDNSZoneInfoEx::Query(LPCTSTR lpszServerName, LPCTSTR lpszZoneName, 
                                      DWORD)
  {
      USES_CONVERSION;
      DNS_RPC_ZONE_INFO* pZoneInfo = NULL;
      LPCSTR lpszAnsiZoneName = W_TO_UTF8(lpszZoneName);
  
      // update original struct
      m_errZoneInfo = ::DnssrvGetZoneInfo(lpszServerName, lpszAnsiZoneName, &pZoneInfo);
  
      if (m_errZoneInfo != 0)
      {
          if (pZoneInfo != NULL)
              ::DnssrvFreeZoneInfo(pZoneInfo);
          return m_errZoneInfo;
      }
      ASSERT(pZoneInfo != NULL);
      FreeInfo();
      m_pZoneInfo = pZoneInfo;
  
      // if we succeeeded and it is an NT 5.0 server, 
      // update additional flags not originally in the zone info struct
  
  /*  
      if (DNS_SRV_MAJOR_VERSION(dwServerVersion) >= DNS_SRV_MAJOR_VERSION_NT_5)
      {
          DWORD dw;
          m_errAllowsDynamicUpdate = ::DnssrvQueryZoneDwordProperty(lpszServerName, 
                                                      lpszAnsiZoneName,
                                                      DNS_REGKEY_ZONE_ALLOW_UPDATE,
                                                      &dw);
          if (m_errAllowsDynamicUpdate == 0)
              m_nAllowsDynamicUpdate = (UINT)dw ;
      }
      return ((m_errZoneInfo == 0) && (m_errAllowsDynamicUpdate == 0)) ?
            0 : (DWORD)-1;
  */
      return (m_errZoneInfo == 0) ? 0 : (DWORD)-1;
  }
  
  void CDNSZoneInfoEx::FreeInfo()
  {
      if (m_pZoneInfo != NULL)
      {
          ::DnssrvFreeZoneInfo(m_pZoneInfo);
          m_pZoneInfo = NULL;
      }
      m_errZoneInfo = 0;
  //  m_errAllowsDynamicUpdate = 0;
  }
  
  
  ///////////////////////////////////////////////////////////////////////////////
  //////////////////// ERROR MESSAGES HANDLING //////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  
  int DNSMessageBox(LPCTSTR lpszText, UINT nType)
  {
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
     CThemeContextActivator activator;
      return ::AfxMessageBox(lpszText, nType);
  }
  
  int DNSMessageBox(UINT nIDPrompt, UINT nType)
  {
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
     CThemeContextActivator activator;
      return ::AfxMessageBox(nIDPrompt, nType);
  }
  
  int DNSErrorDialog(DNS_STATUS err, UINT nErrorMsgID)
  {
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
     CThemeContextActivator activator;
      CString szMsg;
      szMsg.LoadString(nErrorMsgID);
      return DNSErrorDialog(err, szMsg);
  }
  
  void DNSDisplaySystemError(DWORD dwErr)
  {
      AFX_MANAGE_STATE (AfxGetStaticModuleState ());
     CThemeContextActivator activator;
      LPVOID  lpMsgBuf = 0;
          
      FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,    
              NULL,
              dwErr,
              MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
               (LPWSTR) &lpMsgBuf,    0,    NULL);
          
      ::AfxMessageBox ((LPWSTR) lpMsgBuf, MB_OK | MB_ICONINFORMATION);
      // Free the buffer.
      LocalFree (lpMsgBuf);
  }
  
  int DNSErrorDialog(DNS_STATUS err, LPCTSTR lpszErrorMsg)
  {
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
     CThemeContextActivator activator;
  
      CString s;
      CString szError;
      if (CDNSErrorInfo::GetErrorString(err,szError))
      {
          s.Format(_T("%s\n%s"), lpszErrorMsg, (LPCTSTR)szError);
      }
      else
      {
          s.Format(_T("%s\n Error 0x%x"), lpszErrorMsg, err);
      }
      return ::AfxMessageBox(s, MB_OK | MB_ICONERROR);
  }
  
  void DNSCreateErrorMessage(DNS_STATUS err, UINT nErrorMsgID, CString& refszMessage)
  {
      AFX_MANAGE_STATE(AfxGetStaticModuleState());
      CString szMsg;
      szMsg.LoadString(nErrorMsgID);
  
      CString szError;
      if (CDNSErrorInfo::GetErrorString(err,szError))
      {
          refszMessage.Format(_T("%s %s"), szMsg, (LPCTSTR)szError);
      }
      else
      {
          refszMessage.Format(_T("%s Error 0x%x"), szMsg, err);
      }
  }
  
  int DNSConfirmOperation(UINT nMsgID, CTreeNode* p)
  {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    CString szFmt;
    szFmt.LoadString(nMsgID);
    CString szConfirmMsg;
    szConfirmMsg.Format((LPCWSTR)szFmt, p->GetDisplayName());
    return DNSMessageBox(szConfirmMsg, MB_YESNO);
  }
  
  
  BOOL CDNSErrorInfo::GetErrorString(DNS_STATUS err, CString& szError)
  {
    if (GetErrorStringFromTable(err, szError))
      return TRUE;
    return GetErrorStringFromWin32(err, szError);
  }
  
  
  
  BOOL CDNSErrorInfo::GetErrorStringFromWin32(DNS_STATUS err, CString& szError)
  {
    szError.Empty();
    PTSTR ptzSysMsg = NULL;
    int nChars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
                        | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (PTSTR)&ptzSysMsg, 0, NULL);
    if (nChars > 0)
    {
      szError = ptzSysMsg;
      ::LocalFree(ptzSysMsg);
    }
    return (nChars > 0);
  }
  
  
  struct DNS_ERROR_TABLE_ENTRY
  {
      DNS_STATUS dwErr;
      DWORD dwType;
      DWORD dwVal;
  };
  
  #define ERROR_ENTRY_TYPE_END ((DWORD)0)
  #define ERROR_ENTRY_TYPE_STRINGID ((DWORD)1)
  
  #define ERROR_ENTRY_STRINGID(err)           { err , ERROR_ENTRY_TYPE_STRINGID , IDS_##err },
  #define ERROR_ENTRY_STRINGID_EX(err, id)    { err , ERROR_ENTRY_TYPE_STRINGID , id },
  #define END_OF_TABLE_ERROR_ENTRY            { 0 , ERROR_ENTRY_TYPE_END, NULL}
  
  
  BOOL CDNSErrorInfo::GetErrorStringFromTable(DNS_STATUS err, CString& szError)
  {
      static DNS_ERROR_TABLE_ENTRY errorInfo[] =
      {
          //  DNS Specific errors (from WINERROR.H, previously they were in DNS.H)
          //  Response codes mapped to non-colliding errors
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_FORMAT_ERROR)    
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_SERVER_FAILURE)  
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_NAME_ERROR)      
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_NOT_IMPLEMENTED) 
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_REFUSED)         
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_NOTAUTH)         
          ERROR_ENTRY_STRINGID(DNS_ERROR_RCODE_NOTZONE)         
          //  Packet format
          ERROR_ENTRY_STRINGID(DNS_INFO_NO_RECORDS)                         
          ERROR_ENTRY_STRINGID(DNS_ERROR_BAD_PACKET)                        
          ERROR_ENTRY_STRINGID(DNS_ERROR_NO_PACKET)                        
          //  General API errors
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_NAME)                      
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_DATA)                      
  
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_TYPE)                      
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_IP_ADDRESS)                
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_PROPERTY)                  
          //  Zone errors
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_DOES_NOT_EXIST)               
          ERROR_ENTRY_STRINGID(DNS_ERROR_NO_ZONE_INFO)                      
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_ZONE_OPERATION)            
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_CONFIGURATION_ERROR)          
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_HAS_NO_SOA_RECORD)            
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_HAS_NO_NS_RECORDS)            
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_LOCKED)                       
  
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_CREATION_FAILED)              
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_ALREADY_EXISTS)               
          ERROR_ENTRY_STRINGID(DNS_ERROR_AUTOZONE_ALREADY_EXISTS)           
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_ZONE_TYPE)                 
          ERROR_ENTRY_STRINGID(DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP)      
  
          ERROR_ENTRY_STRINGID(DNS_ERROR_ZONE_NOT_SECONDARY)                
          ERROR_ENTRY_STRINGID(DNS_ERROR_NEED_SECONDARY_ADDRESSES)          
          ERROR_ENTRY_STRINGID(DNS_ERROR_WINS_INIT_FAILED)                  
          ERROR_ENTRY_STRINGID(DNS_ERROR_NEED_WINS_SERVERS)                 
          //  Datafile errors
          ERROR_ENTRY_STRINGID(DNS_ERROR_PRIMARY_REQUIRES_DATAFILE)         
          ERROR_ENTRY_STRINGID(DNS_ERROR_INVALID_DATAFILE_NAME)             
          ERROR_ENTRY_STRINGID(DNS_ERROR_DATAFILE_OPEN_FAILURE)             
          ERROR_ENTRY_STRINGID(DNS_ERROR_FILE_WRITEBACK_FAILED)             
          ERROR_ENTRY_STRINGID(DNS_ERROR_DATAFILE_PARSING)                  
          //  Database errors
          ERROR_ENTRY_STRINGID(DNS_ERROR_RECORD_DOES_NOT_EXIST)             
          ERROR_ENTRY_STRINGID(DNS_ERROR_RECORD_FORMAT)                     
          ERROR_ENTRY_STRINGID(DNS_ERROR_NODE_CREATION_FAILED)              
          ERROR_ENTRY_STRINGID(DNS_ERROR_UNKNOWN_RECORD_TYPE)               
          ERROR_ENTRY_STRINGID(DNS_ERROR_RECORD_TIMED_OUT)                  
  
          ERROR_ENTRY_STRINGID(DNS_ERROR_NAME_NOT_IN_ZONE)                  
          ERROR_ENTRY_STRINGID(DNS_ERROR_CNAME_COLLISION)                   
          ERROR_ENTRY_STRINGID(DNS_ERROR_RECORD_ALREADY_EXISTS)             
          ERROR_ENTRY_STRINGID(DNS_ERROR_NAME_DOES_NOT_EXIST)               
  
          ERROR_ENTRY_STRINGID(DNS_WARNING_PTR_CREATE_FAILED)               
          ERROR_ENTRY_STRINGID(DNS_WARNING_DOMAIN_UNDELETED)                
          //  Operation errors
          ERROR_ENTRY_STRINGID(DNS_INFO_AXFR_COMPLETE)                      
          ERROR_ENTRY_STRINGID(DNS_ERROR_AXFR)                              
          ERROR_ENTRY_STRINGID(DNS_ERROR_DS_UNAVAILABLE)
  
          // Generic errors (from WINERROR.H)
          ERROR_ENTRY_STRINGID(RPC_S_SERVER_UNAVAILABLE)
          ERROR_ENTRY_STRINGID_EX(RPC_E_ACCESS_DENIED, IDS_ERROR_ACCESS_DENIED)
          ERROR_ENTRY_STRINGID_EX(ERROR_ACCESS_DENIED, IDS_ERROR_ACCESS_DENIED)
  
      // DS errors from WINERROR.H
          ERROR_ENTRY_STRINGID(DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE)                      
  
          //  end of table
          END_OF_TABLE_ERROR_ENTRY
      };
  
      DNS_ERROR_TABLE_ENTRY* pEntry = errorInfo;
  
      while (pEntry->dwType != ERROR_ENTRY_TYPE_END)
      {
          if (pEntry->dwErr == err)
          {
        if (pEntry->dwType == ERROR_ENTRY_TYPE_STRINGID)
              {
                  return szError.LoadString((UINT)pEntry->dwVal);
              }
          }
          pEntry++;
      }
      szError.Empty();
      return FALSE;
  }
  
  
  //////////////////////////////////////////////////////////////////
  //  Copied from ds\dns\dnslib\record.c by JeffJon on 4/27/2000
  //  modified to support WCHAR
  //
  
  WCHAR  DnsSecurityBase64Mapping[] =
  {
      L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H',
      L'I', L'J', L'K', L'L', L'M', L'N', L'O', L'P',
      L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X',
      L'Y', L'Z', L'a', L'b', L'c', L'd', L'e', L'f',
      L'g', L'h', L'i', L'j', L'k', L'l', L'm', L'n',
      L'o', L'p', L'q', L'r', L's', L't', L'u', L'v',
      L'w', L'x', L'y', L'z', L'0', L'1', L'2', L'3',
      L'4', L'5', L'6', L'7', L'8', L'9', L'+', L'/'
  };
  
  
  WCHAR
  Dns_SecurityBase64CharToBits(IN WCHAR wch64)
  /*++
  
  Routine Description:
  
      Get value of security base64 character.
  
  Arguments:
  
      ch64 -- character in security base64
  
  Return Value:
  
      Value of character, only low 6-bits are significant, high bits zero.
      (-1) if not a base64 character.
  
  --*/
  {
      //  A - Z map to 0 -25
      //  a - z map to 26-51
      //  0 - 9 map to 52-61
      //  + is 62
      //  / is 63
  
      //  could do a lookup table
      //  since we can in general complete mapping with an average of three
      //  comparisons, just encode
  
      if ( wch64 >= L'a' )
      {
          if ( wch64 <= L'z' )
          {
              return static_cast<WCHAR>( wch64 - L'a' + 26 );
          }
      }
      else if ( wch64 >= L'A' )
      {
          if ( wch64 <= L'Z' )
          {
              return static_cast<WCHAR>( wch64 - L'A' );
          }
      }
      else if ( wch64 >= L'0')
      {
          if ( wch64 <= L'9' )
          {
              return static_cast<WCHAR>( wch64 - L'0' + 52 );
          }
          else if ( wch64 == L'=' )
          {
              //*pPadCount++;
              return static_cast<WCHAR>( 0 );
          }
      }
      else if ( wch64 == L'+' )
      {
          return static_cast<WCHAR>( 62 );
      }
      else if ( wch64 == L'/' )
      {
          return static_cast<WCHAR>( 63 );
      }
  
      //  all misses fall here
  
      return static_cast<WCHAR>(-1);
  }
  
  
  DNS_STATUS
  Dns_SecurityBase64StringToKey(
      OUT     PBYTE           pKey,
      OUT     PDWORD          pKeyLength,
      IN      PWCHAR          pchString,
      IN      DWORD           cchLength
      )
  /*++
   
  Routine Description:
   
      Write base64 representation of key to buffer.
   
  Arguments:
   
      pchString   - base64 string to write
   
      cchLength   - length of string
   
      pKey        - ptr to key to write
   
  Return Value:
   
      None
   
  --*/
  {
      DWORD   blend = 0;
      DWORD   index = 0;
      UCHAR   bits;
      PBYTE   pkeyStart = pKey;
   
      //
      //  Mapping is essentially in 24 bit quantums.
      //  Take 4 characters of string key and convert to 3 bytes of binary key.
      //
   
      while ( cchLength-- )
      {
          bits = static_cast<UCHAR>(Dns_SecurityBase64CharToBits( *pchString++ ));
          if ( bits >= 64 )
          {
              return ERROR_INVALID_PARAMETER;
          }
          blend <<= 6;
          blend |= bits;
          index++;
   
          if ( index == 4 )
          {
              index = 0;
   
              //
              //  The first byte of key is top 8 bits of the 24 bit quantum.
              //
   
              *pKey++ = ( UCHAR ) ( ( blend & 0x00ff0000 ) >> 16 );
   
              if ( cchLength || *( pchString - 1 ) != SECURITY_PAD_CHAR )
              {
                  //
                  //  There is no padding so the next two bytes of key
                  //  are bottom 16 bits of the 24 bit quantum. 
                  //
   
                  *pKey++ = ( UCHAR ) ( ( blend & 0x0000ff00 ) >> 8 );
                  *pKey++ = ( UCHAR ) ( blend & 0x000000ff );
              }
              else if ( *( pchString - 2 ) != SECURITY_PAD_CHAR )
              {
                  //
                  //  There is one pad character, so we need to get one
                  //  more byte of key out of the 24 bit quantum. Make sure
                  //  that there are no one bits in the bottom 8 bits of the
                  //  quantum.
                  //
   
                  if ( blend & 0x000000ff )
                  {
                      return ERROR_INVALID_PARAMETER;
                  }
                  *pKey++ = ( UCHAR ) ( ( blend & 0x0000ff00 ) >> 8 );
              }
              else
              {
                  //
                  //  There are two pad characters. Make sure that there
                  //  are no one bits in the bottom 16 bits of the quantum.
                  //
                  
                  if ( blend & 0x0000ffff )
                  {
                      return ERROR_INVALID_PARAMETER;
                  }
              }
              blend = 0;
          }
      }
   
      //
      //  Base64 representation should always be padded out to an even
      //  multiple of 4 characters.
      //
   
      if ( index == 0 )
      {
          //
          //  Key length does not include padding.
          //
   
          *pKeyLength = ( DWORD ) ( pKey - pkeyStart );
          return ERROR_SUCCESS;
      }
      return ERROR_INVALID_PARAMETER;
  }
  
  
  PWSTR
  Dns_SecurityKeyToBase64String(
      IN      PBYTE   pKey,
      IN      DWORD   KeyLength,
      OUT     PWSTR   pchBuffer
      )
  /*++
  
  Routine Description:
  
      Write base64 representation of key to buffer.
  
  Arguments:
  
      pKey        - ptr to key to write
  
      KeyLength   - length of key in bytes
  
      pchBuffer   - buffer to write to (must be adequate for key length)
  
  Return Value:
  
      Ptr to next byte in buffer after string.
  
  --*/
  {
      DWORD   blend = 0;
      DWORD   index = 0;
  
      //
      //  mapping is essentially in 24bit blocks
      //  read three bytes of key and transform into four 64bit characters
      //
  
      while ( KeyLength-- )
      {
          blend <<= 8;
          blend += *pKey++;
          index++;
  
          if ( index == 3)
          {
              *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x00fc0000) >> 18 ];
              *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x0003f000) >> 12 ];
              *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x00000fc0) >> 6 ];
              *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x0000003f) ];
              blend = 0;
              index = 0;
          }
      }
  
      //
      //  key terminates on byte boundary, but not necessarily 24bit block boundary
      //  shift to fill 24bit block filling with zeros
      //  if two bytes written
      //          => write three 6-bits chars and one pad
      //  if one byte written
      //          => write two 6-bits chars and two pads
      //
  
      if ( index )
      {
          blend <<= (8 * (3-index));
  
          *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x00fc0000) >> 18 ];
          *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x0003f000) >> 12 ];
          if ( index == 2 )
          {
              *pchBuffer++ = DnsSecurityBase64Mapping[ (blend & 0x00000fc0) >> 6 ];
          }
          else
          {
              *pchBuffer++ = SECURITY_PAD_CHAR;
          }
          *pchBuffer++ = SECURITY_PAD_CHAR;
      }
  
      return( pchBuffer );
  }
  
  // NOTICE-2002/04/24-artm  ntraid#ntbug9-547641
  // Unused functions need to be removed.  I've left in comments in
  // case they are later needed.
  //DNS_STATUS Dns_SecurityHexToKey(OUT   PBYTE   pKey,
  //                                OUT   PDWORD  pKeyLength,
  //                                IN    PWSTR   pchString,
  //                                IN    DWORD)
  //{
  //  DWORD byteIdx = 0;
  //  size_t strLength = wcslen(pchString);
  //  for (UINT idx = 0; idx < strLength; idx++)
  //  {
  //    CString szTemp;
  //    szTemp = pchString[idx++];
  //    szTemp += pchString[idx];
  //    int result = swscanf(szTemp, L"%x", &(pKey[byteIdx++]));
  //    ASSERT(result == 1);
  //  }
  //
  //  *pKeyLength = byteIdx;
  //  return ERROR_SUCCESS;
  //}
  //
  //void Dns_SecurityKeyToHexString(IN      PBYTE   pKey,
  //                                IN      DWORD   KeyLength,
  //                                OUT     CString& strref)
  //{
  //  strref.Empty();
  //  for (DWORD dwIdx = 0; dwIdx < KeyLength; dwIdx++)
  //  {
  //    CString szTemp;
  //    szTemp = strref;
  //    strref.Format(L"%s%2.2x", szTemp, pKey[dwIdx]);
  //  }
  //}
  
  void TimetToFileTime( time_t t, LPFILETIME pft )
  {
    LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
    pft->dwLowDateTime = (DWORD) ll;
    pft->dwHighDateTime = static_cast<DWORD>(ll >>32);
  }
  
  DWORD FileTimeToTimet(FILETIME* pft)
  {
    LONGLONG ll = 0;
    ll = pft->dwHighDateTime;
    ll = ll << 32;
    ll |= pft->dwLowDateTime;
    ll -= 116444736000000000;
    ll /= 10000000;
  
    return (DWORD)ll;
  }
  
  void ConvertTTLToSystemTime(TIME_ZONE_INFORMATION*,
                              DWORD dwTTL, 
                              SYSTEMTIME* pSysTime)
  {
    time_t ttlTime = static_cast<time_t>(dwTTL);
  
    FILETIME ftTime;
    memset(&ftTime, 0, sizeof(FILETIME));
    TimetToFileTime(ttlTime, &ftTime);
  
    ::FileTimeToSystemTime(&ftTime, pSysTime);
  }
  
  DWORD ConvertSystemTimeToTTL(SYSTEMTIME* pSysTime)
  {
   FILETIME ft;
   ::SystemTimeToFileTime(pSysTime, &ft);
   return FileTimeToTimet(&ft);
  }
  
  BOOL ConvertTTLToLocalTimeString(const DWORD dwTTL,
                                   CString& strref)
  {
     SYSTEMTIME sysLTimeStamp, sysUTimeStamp;
     BOOL bRes = TRUE;
  
     //
     // Convert from seconds since Jan 1, 1970 to SystemTime
     //
     ConvertTTLToSystemTime(NULL, dwTTL, &sysUTimeStamp);
  
     strref.Empty();
  
     //
     // Convert to local SystemTime
     //
     if (!::SystemTimeToTzSpecificLocalTime(NULL, &sysUTimeStamp, &sysLTimeStamp))
     {
        return FALSE;
     }
  
     //
     // Format the string with respect to locale
     //
     PTSTR ptszDate = NULL;
     int cchDate = 0;
  
     //
     // Get the date
     //
     cchDate = GetDateFormat(LOCALE_USER_DEFAULT, 0 , 
                             &sysLTimeStamp, NULL, 
                             ptszDate, 0);
  
     ptszDate = (PTSTR)malloc(sizeof(TCHAR) * cchDate);
  
     if (ptszDate)
     {
        if (GetDateFormat(LOCALE_USER_DEFAULT, 0, 
                       &sysLTimeStamp, NULL, 
                       ptszDate, cchDate))
        {
           strref = ptszDate;
        }
        else
        {
           strref = L"";
           bRes = FALSE;
        }
        free(ptszDate);
     }
     else
     {
        strref = L"";
        bRes = FALSE;
     }
  
     PTSTR ptszTime = NULL;
  
     //
     // Get the time
     //
     cchDate = GetTimeFormat(LOCALE_USER_DEFAULT, 0 , 
                             &sysLTimeStamp, NULL, 
                             ptszTime, 0);
  
     ptszTime = (PTSTR)malloc(sizeof(TCHAR) * cchDate);
     if (ptszTime)
     {
        if (GetTimeFormat(LOCALE_USER_DEFAULT, 0, 
                          &sysLTimeStamp, NULL, 
                          ptszTime, cchDate))
        {
           strref += _T(" ") + CString(ptszTime);
        }
        else
        {
           strref += _T("");
           bRes = FALSE;
        }
        free(ptszTime);
     }
     else
     {
        strref += _T("");
        bRes = FALSE;
     }
  
     return bRes;
  }
  
  // Converts a base64 BLOB into a string by using 4 characters to represent
  // 3 bytes.  Each character is 6bits of the BLOB. If the encoding doesn't
  // end on a 3 byte boundary '=' is used as a pad character
  
  CString Base64BLOBToString(PBYTE blob, DWORD blobSizeInBytes)
  {
    if (!blob ||
        !blobSizeInBytes)
    {
       return L"";
    }
  
    // The largest string will have 4 characters for every 3 bytes in the string
    // I have to add one more before multiplying just in case there are pad characters
    // and another for NULL termination
  
    DWORD stringSize = (((blobSizeInBytes / 3) + 1) * 4) + 1;
  
    WCHAR* szBuffer = new WCHAR[stringSize];
  
    if (!szBuffer)
    {
       return L"";
    }
  
    ::ZeroMemory(szBuffer, stringSize * sizeof(WCHAR));
  
    PWSTR pszEnd = Dns_SecurityKeyToBase64String(blob, 
                                                 blobSizeInBytes,
                                                 szBuffer);
    if (pszEnd != NULL)
    {
      //
      // NULL terminate the string
      //
      *pszEnd = L'\0';
    }
  
    CString result = szBuffer;
    delete[] szBuffer;
  
    return result;
  }
  
  CString Base64BLOBToString(CByteBlob& blob)
  {
     return Base64BLOBToString(blob.GetData(), blob.GetSize());
  }
  
  