text: /*
   * Copyright 2000, International Business Machines Corporation and others.
   * All Rights Reserved.
   *
   * This software has been released under the terms of the IBM Public
   * License.  For details, see the LICENSE file in the top-level source
   * directory or online at http://www.openafs.org/dl/license10.html
   */
  
  
  #include <afsconfig.h>
  #include <afs/param.h>
  #include <afs/stds.h>
  
  #include <roken.h>
  
  #include <windows.h>
  #include <winsock2.h>
  #include <shlwapi.h>
  #include <strsafe.h>
  
  #include "afsd.h"
  #include <WINNT\afssw.h>
  #include <WINNT\afsreg.h>
  
  #include <afs/cellconfig.h>
  
  #include "cm_dns.h"
  #include <afs/afsint.h>
  
  static long cm_ParsePair(char *lineBufferp, char *leftp, char *rightp)
  {
      char *tp;
      char tc;
      int sawEquals;
      int sawBracket;
  
      sawEquals = 0;
      sawBracket = 0;
      for(tp = lineBufferp; *tp; tp++) {
          tc = *tp;
  
  	if (sawBracket) {
  	    if (tc == ']')
  		sawBracket = 0;
  	    continue;
  	}
  
  	/* comment or line end */
          if (tc == '#' || tc == '\r' || tc == '\n')
              break;
  
  	/* square bracket comment -- look for closing delim */
  	if (tc == '[') {
              sawBracket = 1;
              continue;
          }
  
  	/* space or tab */
          if (tc == ' ' || tc == '\t')
              continue;
  
          if (tc == '=') {
              sawEquals = 1;
              continue;
  	}
  
          /* now we have a real character, put it in the appropriate bucket */
          if (sawEquals == 0) {
  	    *leftp++ = tc;
          }
          else {
  	    *rightp++ = tc;
          }
      }
  
      /* null terminate the strings */
      *leftp = 0;
      *rightp = 0;
  
      return 0;	/* and return success */
  }
  
  static int
  IsWindowsModule(const char * name)
  {
      const char * p;
      int i;
  
      /* Do not perform searches for probable Windows modules */
      for (p = name, i=0; *p; p++) {
  	if ( *p == '.' )
  	    i++;
      }
      p = strrchr(name, '.');
      if (p) {
          /* as of 2009-09-04 these are not valid ICANN ccTLDs */
  	if (i == 1 &&
  	    (!cm_stricmp_utf8N(p,".dll") ||
  	     !cm_stricmp_utf8N(p,".exe") ||
  	     !cm_stricmp_utf8N(p,".ini") ||
  	     !cm_stricmp_utf8N(p,".db") ||
  	     !cm_stricmp_utf8N(p,".drv")))
  	    return 1;
      }
      return 0;
  }
  
  /* search for a cell, and either return an error code if we don't find it,
   * or return 0 if we do, in which case we also fill in the addresses in
   * the cellp field.
   *
   * new feature:  we can handle abbreviations and are insensitive to case.
   * If the caller wants the "real" cell name, it puts a non-null pointer in
   * newCellNamep.  Anomaly:  if cellNamep is ambiguous, we may modify
   * newCellNamep but return an error code.
   *
   * Linked Cells: the CellServDB format permits linked cells
   *   >cell [linked-cell] #Description
   *
   * newCellNamep and linkedNamep are required to be CELL_MAXNAMELEN in size.
   */
  long cm_SearchCellFile(char *cellNamep, char *newCellNamep,
                         cm_configProc_t *procp, void *rockp)
  {
      return cm_SearchCellFileEx(cellNamep, newCellNamep, NULL, procp, rockp);
  }
  
  long cm_SearchCellFileEx(char *cellNamep, char *newCellNamep,
                           char *linkedNamep,
                           cm_configProc_t *procp, void *rockp)
  {
      char wdir[MAX_PATH]="";
      FILE *tfilep = NULL, *bestp, *tempp;
      char *tp, *linkp;
      char lineBuffer[257];
      struct hostent *thp;
      char *valuep;
      struct sockaddr_in vlSockAddr;
      int inRightCell = 0;
      int foundCell = 0;
      long code;
      int tracking = 1, partial = 0;
      size_t len;
  
      if ( IsWindowsModule(cellNamep) )
  	return -3;
  
      cm_GetCellServDB(wdir, sizeof(wdir));
      if (*wdir)
          tfilep = fopen(wdir, "r");
  
      if (!tfilep)
          return -2;
  
      bestp = fopen(wdir, "r");
  
  #ifdef CELLSERV_DEBUG
      osi_Log2(afsd_logp,"cm_searchfile fopen handle[%p], wdir[%s]", bestp,
  	     osi_LogSaveString(afsd_logp,wdir));
  #endif
      /* have we seen the cell line for the guy we're looking for? */
      while (1) {
          linkp = NULL;
          tp = fgets(lineBuffer, sizeof(lineBuffer), tfilep);
          if (tracking)
  	    (void) fgets(lineBuffer, sizeof(lineBuffer), bestp);
          if (tp == NULL) {
  	    if (feof(tfilep)) {
  		/* hit EOF */
  		if (partial) {
  		    /*
  		     * found partial match earlier;
  		     * now go back to it
  		     */
  		    tempp = bestp;
  		    bestp = tfilep;
  		    tfilep = tempp;
  		    inRightCell = 1;
  		    partial = 0;
  		    continue;
  		}
  		else {
  		    fclose(tfilep);
  		    fclose(bestp);
  		    return (foundCell? 0 : -3);
  		}
  	    }
          }
  
          /* turn trailing cr or lf into null */
          tp = strrchr(lineBuffer, '\r');
          if (tp) *tp = 0;
          tp = strrchr(lineBuffer, '\n');
          if (tp) *tp = 0;
  
  	/* skip blank lines */
          if (lineBuffer[0] == 0) continue;
  
          /*
           * The format is:
           *   >[cell] [linked-cell] #[Description]
           * where linked-cell and Description are optional
           */
          if (lineBuffer[0] == '>') {
              if (inRightCell) {
                  fclose(tfilep);
                  fclose(bestp);
                  return(foundCell ? 0 : -6);
              }
  
              /*
               * terminate the cellname at the first white space
               * leaving 'tp' pointing to the next string if any
               */
              for (tp = &lineBuffer[1]; tp && !isspace(*tp); tp++);
              if (tp) {
                  *tp = '\0';
                  for (tp++ ;tp && isspace(*tp); tp++);
                  if (*tp != '#') {
                      linkp = tp;
                      for (; tp && !isspace(*tp); tp++);
                      if (tp)
                          *tp = '\0';
                  }
              }
  
  	    /* now see if this is the right cell */
              if (stricmp(lineBuffer+1, cellNamep) == 0) {
  		/* found the cell we're looking for */
  		if (newCellNamep) {
                      if (FAILED(StringCchCopy(newCellNamep, CELL_MAXNAMELEN, lineBuffer+1))) {
                          fclose(tfilep);
                          fclose(bestp);
                          return(-1);
                      }
                      strlwr(newCellNamep);
                  }
                  if (linkedNamep) {
                      if (FAILED(StringCchCopy(linkedNamep, CELL_MAXNAMELEN, linkp ? linkp : ""))) {
                          fclose(tfilep);
                          fclose(bestp);
                          return(-1);
                      }
                      strlwr(linkedNamep);
                  }
                  inRightCell = 1;
  		tracking = 0;
  #ifdef CELLSERV_DEBUG
  		osi_Log2(afsd_logp, "cm_searchfile is cell inRightCell[%p], linebuffer[%s]",
  			 inRightCell, osi_LogSaveString(afsd_logp,lineBuffer));
  #endif
  	    }
  	    else if (cm_stricmp_utf8(lineBuffer+1, cellNamep) == 0) {
  		/* partial match */
  		if (partial) {	/* ambiguous */
  		    fclose(tfilep);
  		    fclose(bestp);
  		    return -5;
  		}
  		if (newCellNamep) {
                      if (FAILED(StringCchCopy(newCellNamep, CELL_MAXNAMELEN, lineBuffer+1))) {
                          fclose(tfilep);
                          fclose(bestp);
                          return(-1);
                      }
                      strlwr(newCellNamep);
                  }
                  if (linkedNamep) {
                      if (FAILED(StringCchCopy(linkedNamep, CELL_MAXNAMELEN, linkp ? linkp : ""))) {
                          fclose(tfilep);
                          fclose(bestp);
                          return(-1);
                      }
                      strlwr(linkedNamep);
                  }
  		inRightCell = 0;
  		tracking = 0;
  		partial = 1;
  	    }
              else inRightCell = 0;
          }
          else {
              valuep = strchr(lineBuffer, '#');
  	    if (valuep == NULL) {
  		fclose(tfilep);
  		fclose(bestp);
  		return -4;
  	    }
              valuep++;	/* skip the "#" */
  
              valuep += strspn(valuep, " \t"); /* skip SP & TAB */
              /*
               * strip spaces and tabs in the end. They should not be there according to CellServDB format
               * so do this just in case
  	     */
              if(FAILED(StringCchLength(valuep, sizeof(lineBuffer), &len))) {
                  fclose(tfilep);
                  fclose(bestp);
                  return(-1);
              } else {
                  len--;
                  while(isspace(valuep[len]))
                      valuep[len--] = '\0';
                  while(isspace(*valuep))
                      valuep++;
              }
  
  	    if (inRightCell) {
                  char hostname[256];
                  int  i, isClone = 0;
  
                  isClone = (lineBuffer[0] == '[');
  
                  /* copy just the first word and ignore trailing white space */
                  for ( i=0; valuep[i] && !isspace(valuep[i]) && i<sizeof(hostname); i++)
                      hostname[i] = valuep[i];
                  hostname[i] = '\0';
  
  		/* add the server to the VLDB list */
                  WSASetLastError(0);
                  thp = gethostbyname(hostname);
  #ifdef CELLSERV_DEBUG
  		osi_Log3(afsd_logp,"cm_searchfile inRightCell thp[%p], valuep[%s], WSAGetLastError[%d]",
  			 thp, osi_LogSaveString(afsd_logp,hostname), WSAGetLastError());
  #endif
  		if (thp) {
                      int foundAddr = 0;
                      for (i=0 ; thp->h_addr_list[i]; i++) {
                          if (thp->h_addrtype != AF_INET)
                              continue;
                          memcpy(&vlSockAddr.sin_addr.s_addr, thp->h_addr_list[i],
                                 sizeof(long));
                          vlSockAddr.sin_port = htons(7003);
                          vlSockAddr.sin_family = AF_INET;
                          /* sin_port supplied by connection code */
                          if (procp)
                              (*procp)(rockp, &vlSockAddr, hostname, 0);
                          foundAddr = 1;
                      }
                      /* if we didn't find a valid address, force the use of the specified one */
                      if (!foundAddr)
                          thp = NULL;
  		}
                  if (!thp) {
                      afs_uint32 ip_addr;
  		    unsigned int c1, c2, c3, c4;
  
                      /* Since there is no gethostbyname() data
  		     * available we will read the IP address
  		     * stored in the CellServDB file
                       */
                      if (isClone)
                          code = sscanf(lineBuffer, "[%u.%u.%u.%u]",
                                        &c1, &c2, &c3, &c4);
                      else
                          code = sscanf(lineBuffer, " %u.%u.%u.%u",
                                        &c1, &c2, &c3, &c4);
                      if (code == 4 && c1<256 && c2<256 && c3<256 && c4<256) {
                          tp = (unsigned char *) &ip_addr;
                          *tp++ = c1;
                          *tp++ = c2;
                          *tp++ = c3;
                          *tp++ = c4;
                          memcpy(&vlSockAddr.sin_addr.s_addr, &ip_addr,
                                  sizeof(long));
                          vlSockAddr.sin_port = htons(7003);
                          vlSockAddr.sin_family = AF_INET;
                          /* sin_port supplied by connection code */
                          if (procp)
                              (*procp)(rockp, &vlSockAddr, hostname, 0);
                      }
                  }
                  foundCell = 1;
              }
          }	/* a vldb line */
      }		/* while loop processing all lines */
  
      /* if for some unknown reason cell is not found, return negative code (-11) ??? */
      return (foundCell) ? 0 : -11;
  }
  
  long
  cm_EnumerateCellFile(afs_uint32 client, cm_enumCellProc_t *procp, void *rockp)
  {
      char wdir[MAX_PATH]="";
      FILE *tfilep = NULL;
      char *tp;
      char lineBuffer[257];
  
      if (!procp)
          return 0;
  
      cm_GetCellServDB(wdir, sizeof(wdir));
      if (*wdir)
          tfilep = fopen(wdir, "r");
  
      if (!tfilep)
          return -2;
  
  #ifdef CELLSERV_DEBUG
      osi_Log2(afsd_logp,"cm_enumfile fopen handle[%p], wdir[%s]", tfilep,
  	     osi_LogSaveString(afsd_logp,wdir));
  #endif
      while (1) {
          tp = fgets(lineBuffer, sizeof(lineBuffer), tfilep);
          if (tp == NULL && feof(tfilep)) {
              /* hit EOF */
              fclose(tfilep);
              return (0);
          }
  
          /* turn trailing cr or lf into null */
          tp = strrchr(lineBuffer, '\r');
          if (tp) *tp = 0;
          tp = strrchr(lineBuffer, '\n');
          if (tp) *tp = 0;
  
  	/* skip blank lines */
          if (lineBuffer[0] == 0)
              continue;
  
          /*
           * The format is:
           *   >[cell] [linked-cell] #[Description]
           * where linked-cell and Description are optional
           * but we are only going to use the initial cell name
           */
          if (lineBuffer[0] == '>') {
              /*
               * terminate the cellname at the first white space
               * leaving 'tp' pointing to the next string if any
               */
              for (tp = &lineBuffer[1]; tp && !isspace(*tp); tp++);
              if (tp)
                  *tp = '\0';
  
              /* Now process the cell */
              (*procp)(rockp, &lineBuffer[1]);
          }
      }		/* while loop processing all lines */
  
      return 0;
  }
  
  /*
   * The CellServDB registry schema is as follows:
   *
   * HKLM\SOFTWARE\OpenAFS\Client\CellServDB\[cellname]\
   *   "LinkedCell"    REG_SZ "[cellname]"
   *   "Description"   REG_SZ "[comment]"
   *   "ForceDNS"      DWORD  {0,1}
   *
   * HKLM\SOFTWARE\OpenAFS\Client\CellServDB\[cellname]\[servername]\
   *   "HostName"      REG_SZ "[hostname]"
   *   "IPv4Address"   REG_SZ "[address]"
   *   "IPv6Address"   REG_SZ "[address]"   <future>
   *   "Comment"       REG_SZ "[comment]"
   *   "Rank"          DWORD  "0..65535"
   *   "Clone"         DWORD  "{0,1}"
   *   "vlserver"      DWORD  "7003"        <future>
   *   "ptserver"      DWORD  "7002"        <future>
   *
   * ForceDNS is implied non-zero if there are no [servername]
   * keys under the [cellname] key.  Otherwise, ForceDNS is zero.
   * If [servername] keys are specified and none of them evaluate
   * to a valid server configuration, the return code is success.
   * This prevents failover to the CellServDB file or DNS.
   */
  long cm_SearchCellRegistry(afs_uint32 client,
                             char *cellNamep, char *newCellNamep,
                             char *linkedNamep,
                             cm_configProc_t *procp, void *rockp)
  {
      HKEY hkCellServDB = 0, hkCellName = 0, hkServerName = 0;
      DWORD dwType, dwSize;
      DWORD dwCells, dwServers, dwForceDNS;
      DWORD dwIndex, dwRank, dwPort;
      unsigned short adminRank;
      unsigned short vlPort;
      LONG code;
      FILETIME ftLastWriteTime;
      char szCellName[CELL_MAXNAMELEN];
      char szServerName[MAXHOSTCHARS];
      char szHostName[MAXHOSTCHARS];
      char szAddr[64];
      struct hostent *thp;
      struct sockaddr_in vlSockAddr;
      char * s;
      size_t len;
  
      if ( IsWindowsModule(cellNamep) )
  	return -1;
  
      /* No Server CellServDB list (yet) */
      if ( !client )
          return CM_ERROR_NOSUCHCELL;
  
      if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                        AFSREG_CLT_OPENAFS_SUBKEY "\\CellServDB",
                        0,
                        KEY_READ|KEY_QUERY_VALUE,
                        &hkCellServDB) != ERROR_SUCCESS)
          return CM_ERROR_NOSUCHCELL;
  
      if (RegOpenKeyEx( hkCellServDB,
                        cellNamep,
                        0,
                        KEY_READ|KEY_QUERY_VALUE,
                        &hkCellName) != ERROR_SUCCESS) {
          BOOL bFound = 0;
  
          /* We did not find an exact match.  Much search for partial matches. */
  
          code = RegQueryInfoKey( hkCellServDB,
                                  NULL,  /* lpClass */
                                  NULL,  /* lpcClass */
                                  NULL,  /* lpReserved */
                                  &dwCells,  /* lpcSubKeys */
                                  NULL,  /* lpcMaxSubKeyLen */
                                  NULL,  /* lpcMaxClassLen */
                                  NULL,  /* lpcValues */
                                  NULL,  /* lpcMaxValueNameLen */
                                  NULL,  /* lpcMaxValueLen */
                                  NULL,  /* lpcbSecurityDescriptor */
                                  &ftLastWriteTime /* lpftLastWriteTime */
                                  );
          if (code != ERROR_SUCCESS)
              dwCells = 0;
  
          /*
           * We search the entire list to ensure that there is only
           * one prefix match.  If there is more than one, we return none.
           */
          for ( dwIndex = 0; dwIndex < dwCells; dwIndex++ ) {
              dwSize = CELL_MAXNAMELEN;
              code = RegEnumKeyEx( hkCellServDB, dwIndex, szCellName, &dwSize, NULL,
                                   NULL, NULL, &ftLastWriteTime);
              if (code != ERROR_SUCCESS)
                  continue;
              szCellName[CELL_MAXNAMELEN-1] = '\0';
              strlwr(szCellName);
  
              /* if not a prefix match, try the next key */
              if (FAILED(StringCchLength(cellNamep, CELL_MAXNAMELEN, &len)))
                  continue;
  
              if (strncmp(cellNamep, szCellName, len))
                  continue;
  
              /* If we have a prefix match and we already found another
               * match, return neither */
              if (hkCellName) {
                  bFound = 0;
                  RegCloseKey( hkCellName);
                  break;
              }
  
              if (RegOpenKeyEx( hkCellServDB,
                                szCellName,
                                0,
                                KEY_READ|KEY_QUERY_VALUE,
                                &hkCellName) != ERROR_SUCCESS)
                  continue;
  
              if (newCellNamep) {
                  if (FAILED(StringCchCopy(newCellNamep, CELL_MAXNAMELEN, cellNamep)))
                      goto done;
              }
              bFound = 1;
          }
  
          if ( !bFound ) {
              RegCloseKey(hkCellServDB);
              return CM_ERROR_NOSUCHCELL;
          }
      } else if (newCellNamep) {
          strncpy(newCellNamep, cellNamep, CELL_MAXNAMELEN);
          newCellNamep[CELL_MAXNAMELEN-1] = '\0';
          strlwr(newCellNamep);
      }
  
      if (linkedNamep) {
          dwSize = CELL_MAXNAMELEN;
          code = RegQueryValueEx(hkCellName, "LinkedCell", NULL, &dwType,
                                  (BYTE *) linkedNamep, &dwSize);
          if (code == ERROR_SUCCESS && dwType == REG_SZ) {
              linkedNamep[CELL_MAXNAMELEN-1] = '\0';
              strlwr(linkedNamep);
          } else {
              linkedNamep[0] = '\0';
          }
      }
  
      /* Check to see if DNS lookups are required */
      dwSize = sizeof(DWORD);
      code = RegQueryValueEx(hkCellName, "ForceDNS", NULL, &dwType,
                              (BYTE *) &dwForceDNS, &dwSize);
      if (code == ERROR_SUCCESS && dwType == REG_DWORD) {
          if (dwForceDNS)
              goto done;
      } else {
          dwForceDNS = 0;
      }
  
      /*
       * Using the defined server list.  Enumerate and populate
       * the server list for the cell.
       */
      code = RegQueryInfoKey( hkCellName,
                              NULL,  /* lpClass */
                              NULL,  /* lpcClass */
                              NULL,  /* lpReserved */
                              &dwServers,  /* lpcSubKeys */
                              NULL,  /* lpcMaxSubKeyLen */
                              NULL,  /* lpcMaxClassLen */
                              NULL,  /* lpcValues */
                              NULL,  /* lpcMaxValueNameLen */
                              NULL,  /* lpcMaxValueLen */
                              NULL,  /* lpcbSecurityDescriptor */
                              &ftLastWriteTime /* lpftLastWriteTime */
                              );
      if (code != ERROR_SUCCESS)
          dwServers = 0;
  
      for ( dwIndex = 0; dwIndex < dwServers; dwIndex++ ) {
          dwSize = MAXHOSTCHARS;
          code = RegEnumKeyEx( hkCellName, dwIndex, szServerName, &dwSize, NULL,
                               NULL, NULL, &ftLastWriteTime);
          if (code != ERROR_SUCCESS)
              continue;
  
          szServerName[MAXHOSTCHARS-1] = '\0';
          if (RegOpenKeyEx( hkCellName,
                            szServerName,
                            0,
                            KEY_READ|KEY_QUERY_VALUE,
                            &hkServerName) != ERROR_SUCCESS)
              continue;
  
          /* We have a handle to a valid server key.  Now we need
           * to add the server to the cell */
  
          /* First, see if there is an alternate hostname specified */
          dwSize = MAXHOSTCHARS;
          code = RegQueryValueEx(hkServerName, "HostName", NULL, &dwType,
                                  (BYTE *) szHostName, &dwSize);
          if (code == ERROR_SUCCESS && dwType == REG_SZ) {
              szHostName[MAXHOSTCHARS-1] = '\0';
              strlwr(szHostName);
              s = szHostName;
          } else {
              s = szServerName;
          }
  
          dwSize = sizeof(DWORD);
          code = RegQueryValueEx(hkServerName, "Rank", NULL, &dwType,
                                  (BYTE *) &dwRank, &dwSize);
          if (code == ERROR_SUCCESS && dwType == REG_DWORD) {
              adminRank = (unsigned short)(dwRank <= 65535 ? dwRank : 65535);
          } else {
              adminRank = 0;
          }
  
          dwSize = sizeof(szAddr);
          code = RegQueryValueEx(hkServerName, "IPv4Address", NULL, &dwType,
                                  (BYTE *) szAddr, &dwSize);
          if (code == ERROR_SUCCESS && dwType == REG_SZ) {
              szAddr[63] = '\0';
          } else {
              szAddr[0] = '\0';
          }
  
          dwSize = sizeof(DWORD);
          code = RegQueryValueEx(hkServerName, "vlserver", NULL, &dwType,
                                  (BYTE *) &dwPort, &dwSize);
          if (code == ERROR_SUCCESS && dwType == REG_DWORD) {
              vlPort = htons((unsigned short)dwPort);
          } else {
              vlPort = htons(7003);
          }
  
          WSASetLastError(0);
          thp = gethostbyname(s);
          if (thp) {
              memcpy(&vlSockAddr.sin_addr.s_addr, thp->h_addr, sizeof(long));
              vlSockAddr.sin_port = htons(7003);
              vlSockAddr.sin_family = AF_INET;
              /* sin_port supplied by connection code */
              if (procp)
                  (*procp)(rockp, &vlSockAddr, s, adminRank);
          } else if (szAddr[0]) {
              afs_uint32 ip_addr;
              unsigned int c1, c2, c3, c4;
  
              /* Since there is no gethostbyname() data
               * available we will read the IP address
               * stored in the CellServDB file
               */
              code = sscanf(szAddr, " %u.%u.%u.%u",
                             &c1, &c2, &c3, &c4);
              if (code == 4 && c1<256 && c2<256 && c3<256 && c4<256) {
                  unsigned char * tp = (unsigned char *) &ip_addr;
                  *tp++ = c1;
                  *tp++ = c2;
                  *tp++ = c3;
                  *tp++ = c4;
                  memcpy(&vlSockAddr.sin_addr.s_addr, &ip_addr,
                          sizeof(long));
                  vlSockAddr.sin_port = vlPort;
                  vlSockAddr.sin_family = AF_INET;
                  /* sin_port supplied by connection code */
                  if (procp)
                      (*procp)(rockp, &vlSockAddr, s, adminRank);
              }
          }
  
          RegCloseKey( hkServerName);
      }
  
    done:
      RegCloseKey(hkCellName);
      RegCloseKey(hkCellServDB);
  
      return ((dwForceDNS || dwServers == 0) ? CM_ERROR_FORCE_DNS_LOOKUP : 0);
  }
  
  /*
   * Following the registry schema listed above, cm_AddCellToRegistry
   * will either create or update the registry configuration data for
   * the specified cellname.
   */
  long cm_AddCellToRegistry( char * cellname,
                             char * linked_cellname,
                             unsigned short vlport,
                             afs_uint32 host_count,
                             char *hostname[],
                             afs_uint32 flags)
  {
      HKEY hkCellServDB = 0, hkCellName = 0, hkServerName = 0;
      DWORD dwPort, dwDisposition;
      long code;
      unsigned int i;
  
      if (RegCreateKeyEx( HKEY_LOCAL_MACHINE,
                          AFSREG_CLT_OPENAFS_SUBKEY "\\CellServDB",
                          0,
                          NULL,
                          REG_OPTION_NON_VOLATILE,
                          KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
                          NULL,
                          &hkCellServDB,
                          &dwDisposition) != ERROR_SUCCESS) {
          code = CM_ERROR_NOACCESS;
          goto done;
      }
  
      /* Perform a recursive deletion of the cellname key */
      SHDeleteKey( hkCellServDB, cellname);
  
      if (RegCreateKeyEx( hkCellServDB,
                          cellname,
                          0,
                          NULL,
                          REG_OPTION_NON_VOLATILE,
                          KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
                          NULL,
                          &hkCellName,
                          &dwDisposition) != ERROR_SUCCESS ||
          (dwDisposition == REG_OPENED_EXISTING_KEY) ) {
          code = CM_ERROR_NOACCESS;
          goto done;
      }
  
      /* If we get here, hkCellName is a handle to an empty key */
  
      if (linked_cellname && linked_cellname[0]) {
          code = RegSetValueEx( hkCellName, "LinkedCell",
                                0, REG_SZ,
                                (BYTE *) linked_cellname, CELL_MAXNAMELEN);
      }
  
      if (flags & CM_CELLFLAG_DNS) {
          DWORD dwForceDNS = 1;
          code = RegSetValueEx( hkCellName, "ForceDNS",
                                0, REG_DWORD,
                                (BYTE *) &dwForceDNS, sizeof(DWORD));
      }
  
      for ( i = 0; i < host_count; i++ ) {
          if (RegCreateKeyEx( hkCellName,
                              hostname[i],
                              0,
                              NULL,
                              REG_OPTION_NON_VOLATILE,
                              KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
                              NULL,
                              &hkServerName,
                              &dwDisposition) != ERROR_SUCCESS ||
               (dwDisposition == REG_OPENED_EXISTING_KEY)) {
              code = CM_ERROR_NOACCESS;
              goto done;
          }
  
          /* We have a handle to a valid server key.  Now we need
           * to add the server to the cell */
  
          /* First, see if there is an alternate hostname specified */
          code = RegSetValueEx( hkServerName, "HostName",
                                0, REG_SZ,
                                (BYTE *) hostname[i], (DWORD)strlen(hostname[i]) + 1);
  
          if (vlport) {
              dwPort = vlport;
              code = RegSetValueEx( hkServerName, "vlserver",
                                    0, REG_DWORD,
                                    (BYTE *) &dwPort, (DWORD)sizeof(DWORD));
          }
          RegCloseKey( hkServerName);
          hkServerName = 0;
      }
  
    done:
      if (hkServerName)
          RegCloseKey(hkServerName);
      if (hkCellName)
          RegCloseKey(hkCellName);
      if (hkCellServDB)
          RegCloseKey(hkCellServDB);
  
      return code;
  }
  
  long cm_EnumerateCellRegistry(afs_uint32 client, cm_enumCellProc_t *procp, void *rockp)
  {
      HKEY hkCellServDB = 0;
      DWORD dwSize;
      DWORD dwCells;
      DWORD dwIndex;
      LONG code;
      FILETIME ftLastWriteTime;
      char szCellName[CELL_MAXNAMELEN];
  	char * subkey = AFSREG_CLT_OPENAFS_SUBKEY "\\CellServDB";
  
      /* No server CellServDB in the registry. */
      if (!client || procp == NULL)
          return 0;
  
      if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  		      subkey,
                        0,
  		      KEY_READ|KEY_QUERY_VALUE,
                        &hkCellServDB) != ERROR_SUCCESS)
          return 0;
  
      code = RegQueryInfoKey( hkCellServDB,
                              NULL,  /* lpClass */
                              NULL,  /* lpcClass */
                              NULL,  /* lpReserved */
                              &dwCells,  /* lpcSubKeys */
                              NULL,  /* lpcMaxSubKeyLen */
                              NULL,  /* lpcMaxClassLen */
                              NULL,  /* lpcValues */
                              NULL,  /* lpcMaxValueNameLen */
                              NULL,  /* lpcMaxValueLen */
                              NULL,  /* lpcbSecurityDescriptor */
                              &ftLastWriteTime /* lpftLastWriteTime */
                              );
      if (code != ERROR_SUCCESS)
          dwCells = 0;
  
      /*
       * Enumerate each Cell and
       */
      for ( dwIndex = 0; dwIndex < dwCells; dwIndex++ ) {
          dwSize = CELL_MAXNAMELEN;
          code = RegEnumKeyEx( hkCellServDB, dwIndex, szCellName, &dwSize, NULL,
                               NULL, NULL, &ftLastWriteTime);
          if (code != ERROR_SUCCESS)
              continue;
          szCellName[CELL_MAXNAMELEN-1] = '\0';
          strlwr(szCellName);
  
          (*procp)(rockp, szCellName);
      }
  
      RegCloseKey(hkCellServDB);
      return 0;
  }
  
  /* newCellNamep is required to be CELL_MAXNAMELEN in size */
  long cm_SearchCellByDNS(char *cellNamep, char *newCellNamep, int *ttl,
                          cm_configProc_t *procp, void *rockp)
  {
      int rc;
      int  cellHostAddrs[AFSMAXCELLHOSTS];
      char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
      unsigned short adminRanks[AFSMAXCELLHOSTS];
      unsigned short ports[AFSMAXCELLHOSTS];      /* network byte order */
      int numServers;
      int i;
      struct sockaddr_in vlSockAddr;
  
  #ifdef CELLSERV_DEBUG
      osi_Log1(afsd_logp,"SearchCellDNS-Doing search for [%s]", osi_LogSaveString(afsd_logp,cellNamep));
  #endif
      /*
       * Do not perform a DNS lookup if the name is
       * either a well-known Windows DLL or directory,
       * or if the name does not contain a top-level
       * domain, or if the file prefix is the afs pioctl
       * file name.
       */
      if ( IsWindowsModule(cellNamep) ||
           cm_FsStrChr(cellNamep, '.') == NULL ||
           strncasecmp(cellNamep, CM_IOCTL_FILENAME_NOSLASH, sizeof(CM_IOCTL_FILENAME_NOSLASH)-1) == 0)
  	return -1;
  
      rc = getAFSServer("afs3-vlserver", "udp", cellNamep, htons(7003),
                        cellHostAddrs, cellHostNames, ports, adminRanks, &numServers, ttl);
      if (rc == 0 && numServers > 0) {     /* found the cell */
  	for (i = 0; i < numServers; i++) {
              memcpy(&vlSockAddr.sin_addr.s_addr, &cellHostAddrs[i],
                     sizeof(long));
              vlSockAddr.sin_port = ports[i];
              vlSockAddr.sin_family = AF_INET;
              if (procp)
                  (*procp)(rockp, &vlSockAddr, cellHostNames[i], adminRanks[i]);
          }
          if (newCellNamep) {
              if(FAILED(StringCchCopy(newCellNamep, CELL_MAXNAMELEN, cellNamep)))
                  return -1;
              strlwr(newCellNamep);
          }
          return 0;   /* found cell */
      }
      else
         return -1;  /* not found */
  }
  
  /* use cm_GetConfigDir() plus AFS_CELLSERVDB to
   * generate the fully qualified name of the CellServDB
   * file.
   */
  long cm_GetCellServDB(char *cellNamep, afs_uint32 len)
  {
      size_t tlen;
  
      cm_GetConfigDir(cellNamep, len);
  
      /* add trailing backslash, if required */
      if (FAILED(StringCchLength(cellNamep, len, &tlen)))
          return(-1L);
  
      if (tlen) {
          if (cellNamep[tlen-1] != '\\') {
              if (FAILED(StringCchCat(cellNamep, len, "\\")))
                  return(-1L);
          }
  
          if (FAILED(StringCchCat(cellNamep, len, AFS_CELLSERVDB)))
              return(-1L);
      }
      return 0;
  }
  
  /* look up the root cell's name in the Registry
   * Input buffer must be at least CELL_MAXNAMELEN
   * in size.  (Defined in cm_cell.h)
   */
  long cm_GetRootCellName(char *cellNamep)
  {
      DWORD code, dummyLen;
      HKEY parmKey;
  
      code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
                          0, KEY_QUERY_VALUE, &parmKey);
      if (code != ERROR_SUCCESS)
          return -1;
  
      dummyLen = CELL_MAXNAMELEN;
      code = RegQueryValueEx(parmKey, "Cell", NULL, NULL,
  				cellNamep, &dummyLen);
      RegCloseKey (parmKey);
      if (code != ERROR_SUCCESS || cellNamep[0] == 0)
          return -1;
  
      return 0;
  }
  
  cm_configFile_t *cm_CommonOpen(char *namep, char *rwp)
  {
      char wdir[MAX_PATH]="";
      FILE *tfilep = NULL;
  
      cm_GetConfigDir(wdir, sizeof(wdir));
      if (*wdir) {
          if (FAILED(StringCchCat(wdir, MAX_PATH, namep)))
              return NULL;
  
          tfilep = fopen(wdir, rwp);
      }
      return ((cm_configFile_t *) tfilep);
  }
  
  long cm_WriteConfigString(char *labelp, char *valuep)
  {
      DWORD code, dummyDisp;
      HKEY parmKey;
      size_t len;
  
      code = RegCreateKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
  			   0, "container", 0, KEY_SET_VALUE, NULL,
  			   &parmKey, &dummyDisp);
      if (code != ERROR_SUCCESS)
  	return -1;
  
      if (FAILED(StringCchLength(valuep, CM_CONFIGDEFAULT_CELLS, &len)))
          return -1;
  
      code = RegSetValueEx(parmKey, labelp, 0, REG_SZ, valuep, (DWORD)(len + 1));
      RegCloseKey (parmKey);
      if (code != ERROR_SUCCESS)
  	return -1;
  
      return 0;
  }
  
  long cm_WriteConfigInt(char *labelp, long value)
  {
      DWORD code, dummyDisp;
      HKEY parmKey;
  
      code = RegCreateKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
                             0, "container", 0, KEY_SET_VALUE, NULL,
                             &parmKey, &dummyDisp);
      if (code != ERROR_SUCCESS)
          return -1;
  
      code = RegSetValueEx(parmKey, labelp, 0, REG_DWORD,
                            (LPBYTE)&value, sizeof(value));
      RegCloseKey (parmKey);
      if (code != ERROR_SUCCESS)
          return -1;
  
      return 0;
  }
  
  cm_configFile_t *cm_OpenCellFile(void)
  {
      cm_configFile_t *cfp;
  
      cfp = cm_CommonOpen(AFS_CELLSERVDB ".new", "w");
      return cfp;
  }
  
  long cm_AppendPrunedCellList(cm_configFile_t *ofp, char *cellNamep)
  {
      cm_configFile_t *tfilep;	/* input file */
      char *tp;
      char lineBuffer[256];
      char *valuep;
      int inRightCell;
      int foundCell;
  
      tfilep = cm_CommonOpen(AFS_CELLSERVDB, "r");
      if (!tfilep)
          return -1;
  
      foundCell = 0;
  
      /* have we seen the cell line for the guy we're looking for? */
      inRightCell = 0;
      while (1) {
          tp = fgets(lineBuffer, sizeof(lineBuffer), (FILE *)tfilep);
          if (tp == NULL) {
              if (feof((FILE *)tfilep)) {
                  /* hit EOF */
                  fclose((FILE *)tfilep);
                  return 0;
              }
          }
  
          /* turn trailing cr or lf into null */
          tp = strchr(lineBuffer, '\r');
          if (tp) *tp = 0;
          tp = strchr(lineBuffer, '\n');
          if (tp) *tp = 0;
  
          /* skip blank lines */
          if (lineBuffer[0] == 0) {
              fprintf((FILE *)ofp, "%s\n", lineBuffer);
              continue;
          }
  
          if (lineBuffer[0] == '>') {
              /* trim off at white space or '#' chars */
              tp = strchr(lineBuffer, ' ');
              if (tp) *tp = 0;
              tp = strchr(lineBuffer, '\t');
              if (tp) *tp = 0;
              tp = strchr(lineBuffer, '#');
              if (tp) *tp = 0;
  
              /* now see if this is the right cell */
              if (strcmp(lineBuffer+1, cellNamep) == 0) {
                  /* found the cell we're looking for */
                  inRightCell = 1;
              }
              else {
                  inRightCell = 0;
                  fprintf((FILE *)ofp, "%s\n", lineBuffer);
              }
          }
          else {
              valuep = strchr(lineBuffer, '#');
              if (valuep == NULL) return -2;
              valuep++;	/* skip the "#" */
              if (!inRightCell) {
                  fprintf((FILE *)ofp, "%s\n", lineBuffer);
              }
          }	/* a vldb line */
      }		/* while loop processing all lines */
  }
  
  long cm_AppendNewCell(cm_configFile_t *filep, char *cellNamep)
  {
      fprintf((FILE *)filep, ">%s\n", cellNamep);
      return 0;
  }
  
  long cm_AppendNewCellLine(cm_configFile_t *filep, char *linep)
  {
      fprintf((FILE *)filep, "%s\n", linep);
      return 0;
  }
  
  long cm_CloseCellFile(cm_configFile_t *filep)
  {
      char wdir[MAX_PATH];
      char sdir[MAX_PATH];
      long code;
      long closeCode = fclose((FILE *)filep);
  
      cm_GetConfigDir(wdir, sizeof(wdir));
      if (FAILED(StringCchCopy(sdir, sizeof(sdir), wdir)))
          return -1;
  
      if (closeCode != 0) {
          /* something went wrong, preserve original database */
          if (FAILED(StringCchCat(wdir, sizeof(wdir), AFS_CELLSERVDB ".new")))
              return -1;
          unlink(wdir);
          return closeCode;
      }
  
      if (FAILED(StringCchCat(wdir, sizeof(wdir), AFS_CELLSERVDB)))
          return -1;
      if (FAILED(StringCchCat(sdir, sizeof(sdir), AFS_CELLSERVDB ".new"))) /* new file */
          return -1;
  
      unlink(sdir);			/* delete old file */
  
      code = rename(sdir, wdir);	/* do the rename */
  
      if (code)
          code = errno;
  
      return code;
  }
  
  void cm_GetConfigDir(char *dir, afs_uint32 len)
  {
      char * dirp = NULL;
  
      if (!afssw_GetClientCellServDBDir(&dirp)) {
          if (FAILED(StringCchCopy(dir, len, dirp)))
              return;
          free(dirp);
      } else {
          dir[0] = '\0';
      }
  }
  