Windows: do not request KEY_WRITE privilege if not required
[openafs.git] / src / WINNT / afsd / cm_config.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <windows.h>
11 #include <winsock2.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "afsd.h"
17 #include <WINNT\afssw.h>
18 #include <WINNT\afsreg.h>
19
20 #include <afs/param.h>
21 #include <afs/stds.h>
22 #include <afs/cellconfig.h>
23
24 #include "cm_dns.h"
25 #include <afs/afsint.h>
26
27 static long cm_ParsePair(char *lineBufferp, char *leftp, char *rightp)
28 {
29     char *tp;
30     char tc;
31     int sawEquals;
32     int sawBracket;
33         
34     sawEquals = 0;
35     sawBracket = 0;
36     for(tp = lineBufferp; *tp; tp++) {
37         tc = *tp;
38
39         if (sawBracket) {
40             if (tc == ']')
41                 sawBracket = 0;
42             continue;
43         }
44
45         /* comment or line end */
46         if (tc == '#' || tc == '\r' || tc == '\n') 
47             break;
48
49         /* square bracket comment -- look for closing delim */
50         if (tc == '[') {
51             sawBracket = 1; 
52             continue;
53         }       
54
55         /* space or tab */
56         if (tc == ' ' || tc == '\t') 
57             continue;
58
59         if (tc == '=') {
60             sawEquals = 1;
61             continue;
62         }
63
64         /* now we have a real character, put it in the appropriate bucket */
65         if (sawEquals == 0) {
66             *leftp++ = tc;
67         }       
68         else {  
69             *rightp++ = tc;
70         }
71     }
72
73     /* null terminate the strings */
74     *leftp = 0;
75     *rightp = 0;
76
77     return 0;   /* and return success */
78 }
79
80 static int
81 IsWindowsModule(const char * name)
82 {
83     const char * p;
84     int i;
85
86     /* Do not perform searches for probable Windows modules */
87     for (p = name, i=0; *p; p++) {
88         if ( *p == '.' )
89             i++;
90     }
91     p = strrchr(name, '.');
92     if (p) {
93         /* as of 2009-09-04 these are not valid ICANN ccTLDs */
94         if (i == 1 && 
95             (!cm_stricmp_utf8N(p,".dll") ||
96              !cm_stricmp_utf8N(p,".exe") ||
97              !cm_stricmp_utf8N(p,".ini") ||
98              !cm_stricmp_utf8N(p,".db") ||
99              !cm_stricmp_utf8N(p,".drv")))
100             return 1;
101     }
102     return 0;
103 }
104
105 /* search for a cell, and either return an error code if we don't find it,
106  * or return 0 if we do, in which case we also fill in the addresses in
107  * the cellp field.
108  *
109  * new feature:  we can handle abbreviations and are insensitive to case.
110  * If the caller wants the "real" cell name, it puts a non-null pointer in
111  * newCellNamep.  Anomaly:  if cellNamep is ambiguous, we may modify
112  * newCellNamep but return an error code.
113  *
114  * Linked Cells: the CellServDB format permits linked cells
115  *   >cell [linked-cell] #Description
116  *
117  * newCellNamep and linkedNamep are required to be CELL_MAXNAMELEN in size.
118  */
119 long cm_SearchCellFile(char *cellNamep, char *newCellNamep,
120                        cm_configProc_t *procp, void *rockp)
121 {
122     return cm_SearchCellFileEx(cellNamep, newCellNamep, NULL, procp, rockp);
123 }
124
125 long cm_SearchCellFileEx(char *cellNamep, char *newCellNamep,
126                          char *linkedNamep,
127                          cm_configProc_t *procp, void *rockp)
128 {
129     char wdir[MAX_PATH]="";
130     FILE *tfilep = NULL, *bestp, *tempp;
131     char *tp, *linkp;
132     char lineBuffer[257];
133     struct hostent *thp;
134     char *valuep;
135     struct sockaddr_in vlSockAddr;
136     int inRightCell = 0;
137     int foundCell = 0;
138     long code;
139     int tracking = 1, partial = 0;
140
141     if ( IsWindowsModule(cellNamep) )
142         return -3;
143
144     cm_GetCellServDB(wdir, sizeof(wdir));
145     if (*wdir)
146         tfilep = fopen(wdir, "r");
147
148     if (!tfilep) 
149         return -2;
150
151     bestp = fopen(wdir, "r");
152     
153 #ifdef CELLSERV_DEBUG
154     osi_Log2(afsd_logp,"cm_searchfile fopen handle[%p], wdir[%s]", bestp, 
155              osi_LogSaveString(afsd_logp,wdir));
156 #endif
157     /* have we seen the cell line for the guy we're looking for? */
158     while (1) {
159         linkp = NULL;
160         tp = fgets(lineBuffer, sizeof(lineBuffer), tfilep);
161         if (tracking)
162             (void) fgets(lineBuffer, sizeof(lineBuffer), bestp);
163         if (tp == NULL) {
164             if (feof(tfilep)) {
165                 /* hit EOF */
166                 if (partial) {
167                     /*
168                      * found partial match earlier;
169                      * now go back to it
170                      */
171                     tempp = bestp;
172                     bestp = tfilep;
173                     tfilep = tempp;
174                     inRightCell = 1;
175                     partial = 0;
176                     continue;
177                 }
178                 else {
179                     fclose(tfilep);
180                     fclose(bestp);
181                     return (foundCell? 0 : -3);
182                 }
183             }
184         }       
185
186         /* turn trailing cr or lf into null */
187         tp = strrchr(lineBuffer, '\r');
188         if (tp) *tp = 0;
189         tp = strrchr(lineBuffer, '\n');
190         if (tp) *tp = 0;
191
192         /* skip blank lines */
193         if (lineBuffer[0] == 0) continue;
194
195         /*
196          * The format is:
197          *   >[cell] [linked-cell] #[Description]
198          * where linked-cell and Description are optional
199          */
200         if (lineBuffer[0] == '>') {
201             if (inRightCell) {
202                 fclose(tfilep);
203                 fclose(bestp);
204                 return(foundCell ? 0 : -6);
205             }
206
207             /* 
208              * terminate the cellname at the first white space
209              * leaving 'tp' pointing to the next string if any
210              */
211             for (tp = &lineBuffer[1]; tp && !isspace(*tp); tp++);
212             if (tp) {
213                 *tp = '\0';
214                 for (tp++ ;tp && isspace(*tp); tp++);
215                 if (*tp != '#') {
216                     linkp = tp;
217                     for (; tp && !isspace(*tp); tp++);
218                     if (tp) 
219                         *tp = '\0';
220                 }
221             }
222
223             /* now see if this is the right cell */
224             if (stricmp(lineBuffer+1, cellNamep) == 0) {
225                 /* found the cell we're looking for */
226                 if (newCellNamep) {
227                     strncpy(newCellNamep, lineBuffer+1,CELL_MAXNAMELEN);
228                     newCellNamep[CELL_MAXNAMELEN-1] = '\0';
229                     strlwr(newCellNamep);
230                 }
231                 if (linkedNamep) {
232                     strncpy(linkedNamep, linkp ? linkp : "", CELL_MAXNAMELEN);
233                     linkedNamep[CELL_MAXNAMELEN-1] = '\0';
234                     strlwr(linkedNamep);
235                 }
236                 inRightCell = 1;
237                 tracking = 0;
238 #ifdef CELLSERV_DEBUG                
239                 osi_Log2(afsd_logp, "cm_searchfile is cell inRightCell[%p], linebuffer[%s]",
240                          inRightCell, osi_LogSaveString(afsd_logp,lineBuffer));
241 #endif
242             }
243             else if (cm_stricmp_utf8(lineBuffer+1, cellNamep) == 0) {
244                 /* partial match */
245                 if (partial) {  /* ambiguous */
246                     fclose(tfilep);
247                     fclose(bestp);
248                     return -5;
249                 }
250                 if (newCellNamep) {
251                     strncpy(newCellNamep, lineBuffer+1,CELL_MAXNAMELEN);
252                     newCellNamep[CELL_MAXNAMELEN-1] = '\0';
253                     strlwr(newCellNamep);
254                 }
255                 if (linkedNamep) {
256                     strncpy(linkedNamep, linkp ? linkp : "", CELL_MAXNAMELEN);
257                     linkedNamep[CELL_MAXNAMELEN-1] = '\0';
258                     strlwr(linkedNamep);
259                 }
260                 inRightCell = 0;
261                 tracking = 0;
262                 partial = 1;
263             }
264             else inRightCell = 0;
265         }
266         else {
267             valuep = strchr(lineBuffer, '#');
268             if (valuep == NULL) {
269                 fclose(tfilep);
270                 fclose(bestp);
271                 return -4;
272             }
273             valuep++;   /* skip the "#" */
274
275             valuep += strspn(valuep, " \t"); /* skip SP & TAB */
276             /* strip spaces and tabs in the end. They should not be there according to CellServDB format
277              * so do this just in case                        
278              */
279             while (valuep[strlen(valuep) - 1] == ' ' || valuep[strlen(valuep) - 1] == '\t') 
280                 valuep[strlen(valuep) - 1] = '\0';
281
282             if (inRightCell) {
283                 char hostname[256];
284                 int  i, isClone = 0;
285
286                 isClone = (lineBuffer[0] == '[');
287
288                 /* copy just the first word and ignore trailing white space */
289                 for ( i=0; valuep[i] && !isspace(valuep[i]) && i<sizeof(hostname); i++)
290                     hostname[i] = valuep[i];
291                 hostname[i] = '\0';
292
293                 /* add the server to the VLDB list */
294                 WSASetLastError(0);
295                 thp = gethostbyname(hostname);
296 #ifdef CELLSERV_DEBUG
297                 osi_Log3(afsd_logp,"cm_searchfile inRightCell thp[%p], valuep[%s], WSAGetLastError[%d]",
298                          thp, osi_LogSaveString(afsd_logp,hostname), WSAGetLastError());
299 #endif
300                 if (thp) {
301                     int foundAddr = 0;
302                     for (i=0 ; thp->h_addr_list[i]; i++) {
303                         if (thp->h_addrtype != AF_INET)
304                             continue;
305                         memcpy(&vlSockAddr.sin_addr.s_addr, thp->h_addr_list[i],
306                                sizeof(long));
307                         vlSockAddr.sin_port = htons(7003);
308                         vlSockAddr.sin_family = AF_INET;
309                         /* sin_port supplied by connection code */
310                         if (procp)
311                             (*procp)(rockp, &vlSockAddr, hostname, 0);
312                         foundAddr = 1;
313                     }
314                     /* if we didn't find a valid address, force the use of the specified one */
315                     if (!foundAddr)
316                         thp = NULL;
317                 }
318                 if (!thp) {
319                     afs_uint32 ip_addr;
320                     unsigned int c1, c2, c3, c4;
321                     
322                     /* Since there is no gethostbyname() data 
323                      * available we will read the IP address
324                      * stored in the CellServDB file
325                      */
326                     if (isClone)
327                         code = sscanf(lineBuffer, "[%u.%u.%u.%u]",
328                                       &c1, &c2, &c3, &c4);
329                     else
330                         code = sscanf(lineBuffer, " %u.%u.%u.%u",
331                                       &c1, &c2, &c3, &c4);
332                     if (code == 4 && c1<256 && c2<256 && c3<256 && c4<256) {
333                         tp = (unsigned char *) &ip_addr;
334                         *tp++ = c1;
335                         *tp++ = c2;
336                         *tp++ = c3;
337                         *tp++ = c4;
338                         memcpy(&vlSockAddr.sin_addr.s_addr, &ip_addr,
339                                 sizeof(long));
340                         vlSockAddr.sin_port = htons(7003);
341                         vlSockAddr.sin_family = AF_INET;
342                         /* sin_port supplied by connection code */
343                         if (procp)
344                             (*procp)(rockp, &vlSockAddr, hostname, 0);
345                     }
346                 }
347                 foundCell = 1;
348             }
349         }       /* a vldb line */
350     }           /* while loop processing all lines */
351
352     /* if for some unknown reason cell is not found, return negative code (-11) ??? */
353     return (foundCell) ? 0 : -11;
354 }
355
356 /*
357  * The CellServDB registry schema is as follows:
358  *
359  * HKLM\SOFTWARE\OpenAFS\Client\CellServDB\[cellname]\
360  *   "LinkedCell"    REG_SZ "[cellname]" 
361  *   "Description"   REG_SZ "[comment]"
362  *   "ForceDNS"      DWORD  {0,1}
363  *
364  * HKLM\SOFTWARE\OpenAFS\Client\CellServDB\[cellname]\[servername]\
365  *   "HostName"      REG_SZ "[hostname]" 
366  *   "IPv4Address"   REG_SZ "[address]" 
367  *   "IPv6Address"   REG_SZ "[address]"   <future>
368  *   "Comment"       REG_SZ "[comment]"
369  *   "Rank"          DWORD  "0..65535"
370  *   "Clone"         DWORD  "{0,1}"
371  *   "vlserver"      DWORD  "7003"        <future>
372  *   "ptserver"      DWORD  "7002"        <future>
373  *
374  * ForceDNS is implied non-zero if there are no [servername]
375  * keys under the [cellname] key.  Otherwise, ForceDNS is zero.
376  * If [servername] keys are specified and none of them evaluate
377  * to a valid server configuration, the return code is success.
378  * This prevents failover to the CellServDB file or DNS.
379  */
380 long cm_SearchCellRegistry(afs_uint32 client, 
381                            char *cellNamep, char *newCellNamep,
382                            char *linkedNamep,
383                            cm_configProc_t *procp, void *rockp)
384 {
385     HKEY hkCellServDB = 0, hkCellName = 0, hkServerName = 0;
386     DWORD dwType, dwSize;
387     DWORD dwCells, dwServers, dwForceDNS;
388     DWORD dwIndex, dwRank, dwPort;
389     unsigned short ipRank;
390     unsigned short vlPort;
391     LONG code;
392     FILETIME ftLastWriteTime;
393     char szCellName[CELL_MAXNAMELEN];
394     char szServerName[MAXHOSTCHARS];
395     char szHostName[MAXHOSTCHARS];
396     char szAddr[64];
397     struct hostent *thp;
398     struct sockaddr_in vlSockAddr;
399     char * s;
400
401     if ( IsWindowsModule(cellNamep) )
402         return -1;
403
404     /* No Server CellServDB list (yet) */
405     if ( !client )
406         return CM_ERROR_NOSUCHCELL;
407
408     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
409                       AFSREG_CLT_OPENAFS_SUBKEY "\\CellServDB",
410                       0,
411                       KEY_READ|KEY_QUERY_VALUE,
412                       &hkCellServDB) != ERROR_SUCCESS)
413         return CM_ERROR_NOSUCHCELL;
414
415     if (RegOpenKeyEx( hkCellServDB, 
416                       cellNamep,
417                       0,
418                       KEY_READ|KEY_QUERY_VALUE,
419                       &hkCellName) != ERROR_SUCCESS) {
420         BOOL bFound = 0;
421
422         /* We did not find an exact match.  Much search for partial matches. */
423
424         code = RegQueryInfoKey( hkCellServDB,
425                                 NULL,  /* lpClass */
426                                 NULL,  /* lpcClass */
427                                 NULL,  /* lpReserved */
428                                 &dwCells,  /* lpcSubKeys */
429                                 NULL,  /* lpcMaxSubKeyLen */
430                                 NULL,  /* lpcMaxClassLen */
431                                 NULL,  /* lpcValues */
432                                 NULL,  /* lpcMaxValueNameLen */
433                                 NULL,  /* lpcMaxValueLen */
434                                 NULL,  /* lpcbSecurityDescriptor */
435                                 &ftLastWriteTime /* lpftLastWriteTime */
436                                 );
437         if (code != ERROR_SUCCESS)
438             dwCells = 0;
439
440         /* 
441          * We search the entire list to ensure that there is only
442          * one prefix match.  If there is more than one, we return none.
443          */
444         for ( dwIndex = 0; dwIndex < dwCells; dwIndex++ ) {
445             dwSize = CELL_MAXNAMELEN;
446             code = RegEnumKeyEx( hkCellServDB, dwIndex, szCellName, &dwSize, NULL, 
447                                  NULL, NULL, &ftLastWriteTime);
448             if (code != ERROR_SUCCESS)
449                 continue;
450             szCellName[CELL_MAXNAMELEN-1] = '\0';
451             strlwr(szCellName);
452
453             /* if not a prefix match, try the next key */
454             if (strncmp(cellNamep, szCellName, strlen(cellNamep)))
455                 continue;
456
457             /* If we have a prefix match and we already found another
458              * match, return neither */
459             if (hkCellName) {
460                 bFound = 0;
461                 RegCloseKey( hkCellName);
462                 break;
463             }
464
465             if (RegOpenKeyEx( hkCellServDB, 
466                               szCellName,
467                               0,
468                               KEY_READ|KEY_QUERY_VALUE,
469                               &hkCellName) != ERROR_SUCCESS)
470                 continue;
471
472             if (newCellNamep) {
473                 strncpy(newCellNamep, szCellName, CELL_MAXNAMELEN);
474                 newCellNamep[CELL_MAXNAMELEN-1] = '\0';
475             }
476             bFound = 1;
477         }
478
479         if ( !bFound ) {
480             RegCloseKey(hkCellServDB);
481             return CM_ERROR_NOSUCHCELL;
482         }
483     } else if (newCellNamep) {
484         strncpy(newCellNamep, cellNamep, CELL_MAXNAMELEN);
485         newCellNamep[CELL_MAXNAMELEN-1] = '\0';
486         strlwr(newCellNamep);
487     }
488
489     if (linkedNamep) {
490         dwSize = CELL_MAXNAMELEN;
491         code = RegQueryValueEx(hkCellName, "LinkedCell", NULL, &dwType,
492                                 (BYTE *) linkedNamep, &dwSize);
493         if (code == ERROR_SUCCESS && dwType == REG_SZ) {
494             linkedNamep[CELL_MAXNAMELEN-1] = '\0';
495             strlwr(linkedNamep);
496         } else {
497             linkedNamep[0] = '\0';
498         }
499     }
500
501     /* Check to see if DNS lookups are required */
502     dwSize = sizeof(DWORD);
503     code = RegQueryValueEx(hkCellName, "ForceDNS", NULL, &dwType,
504                             (BYTE *) &dwForceDNS, &dwSize);
505     if (code == ERROR_SUCCESS && dwType == REG_DWORD) {
506         if (dwForceDNS)
507             goto done;
508     } else {
509         dwForceDNS = 0;
510     }
511
512     /* 
513      * Using the defined server list.  Enumerate and populate
514      * the server list for the cell.
515      */
516     code = RegQueryInfoKey( hkCellName,
517                             NULL,  /* lpClass */
518                             NULL,  /* lpcClass */
519                             NULL,  /* lpReserved */
520                             &dwServers,  /* lpcSubKeys */
521                             NULL,  /* lpcMaxSubKeyLen */
522                             NULL,  /* lpcMaxClassLen */
523                             NULL,  /* lpcValues */
524                             NULL,  /* lpcMaxValueNameLen */
525                             NULL,  /* lpcMaxValueLen */
526                             NULL,  /* lpcbSecurityDescriptor */
527                             &ftLastWriteTime /* lpftLastWriteTime */
528                             );
529     if (code != ERROR_SUCCESS)
530         dwServers = 0;
531
532     for ( dwIndex = 0; dwIndex < dwServers; dwIndex++ ) {
533         dwSize = MAXHOSTCHARS;
534         code = RegEnumKeyEx( hkCellName, dwIndex, szServerName, &dwSize, NULL, 
535                              NULL, NULL, &ftLastWriteTime);
536         if (code != ERROR_SUCCESS)
537             continue;
538
539         szServerName[MAXHOSTCHARS-1] = '\0';
540         if (RegOpenKeyEx( hkCellName, 
541                           szServerName,
542                           0,
543                           KEY_READ|KEY_QUERY_VALUE,
544                           &hkServerName) != ERROR_SUCCESS)
545             continue;
546
547         /* We have a handle to a valid server key.  Now we need 
548          * to add the server to the cell */
549         
550         /* First, see if there is an alternate hostname specified */
551         dwSize = MAXHOSTCHARS;
552         code = RegQueryValueEx(hkServerName, "HostName", NULL, &dwType,
553                                 (BYTE *) szHostName, &dwSize);
554         if (code == ERROR_SUCCESS && dwType == REG_SZ) {
555             szHostName[MAXHOSTCHARS-1] = '\0';
556             strlwr(szHostName);
557             s = szHostName;
558         } else {
559             s = szServerName;
560         }
561
562         dwSize = sizeof(DWORD);
563         code = RegQueryValueEx(hkServerName, "Rank", NULL, &dwType,
564                                 (BYTE *) &dwRank, &dwSize);
565         if (code == ERROR_SUCCESS && dwType == REG_DWORD) {
566             ipRank = (unsigned short)(dwRank <= 65535 ? dwRank : 65535);
567         } else {
568             ipRank = 0;
569         }
570
571         dwSize = sizeof(szAddr);
572         code = RegQueryValueEx(hkServerName, "IPv4Address", NULL, &dwType,
573                                 (BYTE *) szAddr, &dwSize);
574         if (code == ERROR_SUCCESS && dwType == REG_SZ) {
575             szAddr[63] = '\0';
576         } else {
577             szAddr[0] = '\0';
578         }
579
580         dwSize = sizeof(DWORD);
581         code = RegQueryValueEx(hkServerName, "vlserver", NULL, &dwType,
582                                 (BYTE *) &dwPort, &dwSize);
583         if (code == ERROR_SUCCESS && dwType == REG_DWORD) {
584             vlPort = htons((unsigned short)dwPort);
585         } else {
586             vlPort = htons(7003);
587         }
588
589         WSASetLastError(0);
590         thp = gethostbyname(s);
591         if (thp) {
592             memcpy(&vlSockAddr.sin_addr.s_addr, thp->h_addr, sizeof(long));
593             vlSockAddr.sin_port = htons(7003);
594             vlSockAddr.sin_family = AF_INET;
595             /* sin_port supplied by connection code */
596             if (procp)
597                 (*procp)(rockp, &vlSockAddr, s, ipRank);
598         } else if (szAddr[0]) {
599             afs_uint32 ip_addr;
600             unsigned int c1, c2, c3, c4;
601
602             /* Since there is no gethostbyname() data 
603              * available we will read the IP address
604              * stored in the CellServDB file
605              */
606             code = sscanf(szAddr, " %u.%u.%u.%u",
607                            &c1, &c2, &c3, &c4);
608             if (code == 4 && c1<256 && c2<256 && c3<256 && c4<256) {
609                 unsigned char * tp = (unsigned char *) &ip_addr;
610                 *tp++ = c1;
611                 *tp++ = c2;
612                 *tp++ = c3;
613                 *tp++ = c4;
614                 memcpy(&vlSockAddr.sin_addr.s_addr, &ip_addr,
615                         sizeof(long));
616                 vlSockAddr.sin_port = vlPort;
617                 vlSockAddr.sin_family = AF_INET;
618                 /* sin_port supplied by connection code */
619                 if (procp)
620                     (*procp)(rockp, &vlSockAddr, s, ipRank);
621             }
622         }
623
624         RegCloseKey( hkServerName);
625     }
626
627   done:
628     RegCloseKey(hkCellName);
629     RegCloseKey(hkCellServDB);
630
631     return ((dwForceDNS || dwServers == 0) ? CM_ERROR_FORCE_DNS_LOOKUP : 0);
632 }
633
634 long cm_EnumerateCellRegistry(afs_uint32 client, cm_enumCellRegistryProc_t *procp, void *rockp)
635 {
636     HKEY hkCellServDB = 0;
637     DWORD dwSize;
638     DWORD dwCells;
639     DWORD dwIndex;
640     LONG code;
641     FILETIME ftLastWriteTime;
642     char szCellName[CELL_MAXNAMELEN];
643
644     /* No server CellServDB in the registry. */
645     if (!client || procp == NULL)
646         return 0;
647
648     if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
649                       AFSREG_CLT_OPENAFS_SUBKEY "\\CellServDB",
650                       0,
651                       KEY_READ|KEY_WRITE|KEY_QUERY_VALUE,
652                       &hkCellServDB) != ERROR_SUCCESS)
653         return 0;
654
655     code = RegQueryInfoKey( hkCellServDB,
656                             NULL,  /* lpClass */
657                             NULL,  /* lpcClass */
658                             NULL,  /* lpReserved */
659                             &dwCells,  /* lpcSubKeys */
660                             NULL,  /* lpcMaxSubKeyLen */
661                             NULL,  /* lpcMaxClassLen */
662                             NULL,  /* lpcValues */
663                             NULL,  /* lpcMaxValueNameLen */
664                             NULL,  /* lpcMaxValueLen */
665                             NULL,  /* lpcbSecurityDescriptor */
666                             &ftLastWriteTime /* lpftLastWriteTime */
667                             );
668     if (code != ERROR_SUCCESS)
669         dwCells = 0;
670
671     /* 
672      * Enumerate each Cell and 
673      */
674     for ( dwIndex = 0; dwIndex < dwCells; dwIndex++ ) {
675         dwSize = CELL_MAXNAMELEN;
676         code = RegEnumKeyEx( hkCellServDB, dwIndex, szCellName, &dwSize, NULL, 
677                              NULL, NULL, &ftLastWriteTime);
678         if (code != ERROR_SUCCESS)
679             continue;
680         szCellName[CELL_MAXNAMELEN-1] = '\0';
681         strlwr(szCellName);
682
683         (*procp)(rockp, szCellName);
684     }
685
686     RegCloseKey(hkCellServDB);
687     return 0;
688 }
689
690 /* newCellNamep is required to be CELL_MAXNAMELEN in size */
691 long cm_SearchCellByDNS(char *cellNamep, char *newCellNamep, int *ttl,
692                         cm_configProc_t *procp, void *rockp)
693 {
694     int rc;
695     int  cellHostAddrs[AFSMAXCELLHOSTS];
696     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
697     unsigned short ipRanks[AFSMAXCELLHOSTS];
698     unsigned short ports[AFSMAXCELLHOSTS];      /* network byte order */
699     int numServers;
700     int i;
701     struct sockaddr_in vlSockAddr;
702 #ifdef CELLSERV_DEBUG
703     osi_Log1(afsd_logp,"SearchCellDNS-Doing search for [%s]", osi_LogSaveString(afsd_logp,cellNamep));
704 #endif
705     /*
706      * Do not perform a DNS lookup if the name is
707      * either a well-known Windows DLL or directory,
708      * or if the name does not contain a top-level
709      * domain, or if the file prefix is the afs pioctl
710      * file name.
711      */
712     if ( IsWindowsModule(cellNamep) ||
713          cm_FsStrChr(cellNamep, '.') == NULL ||
714          strncasecmp(cellNamep, CM_IOCTL_FILENAME_NOSLASH, strlen(CM_IOCTL_FILENAME_NOSLASH)) == 0)
715         return -1;
716
717     rc = getAFSServer("afs3-vlserver", "udp", cellNamep, htons(7003),
718                       cellHostAddrs, cellHostNames, ports, ipRanks, &numServers, ttl);
719     if (rc == 0 && numServers > 0) {     /* found the cell */
720         for (i = 0; i < numServers; i++) {
721             memcpy(&vlSockAddr.sin_addr.s_addr, &cellHostAddrs[i],
722                    sizeof(long));
723             vlSockAddr.sin_port = ports[i];
724             vlSockAddr.sin_family = AF_INET;
725             if (procp)
726                 (*procp)(rockp, &vlSockAddr, cellHostNames[i], ipRanks[i]);
727         }
728         if (newCellNamep) {
729             strncpy(newCellNamep,cellNamep,CELL_MAXNAMELEN);
730             newCellNamep[CELL_MAXNAMELEN-1] = '\0';
731             strlwr(newCellNamep);
732         }
733         return 0;   /* found cell */
734     }
735     else
736        return -1;  /* not found */
737 }
738
739 /* use cm_GetConfigDir() plus AFS_CELLSERVDB to 
740  * generate the fully qualified name of the CellServDB 
741  * file.
742  */
743 long cm_GetCellServDB(char *cellNamep, afs_uint32 len)
744 {
745     size_t tlen;
746     
747     cm_GetConfigDir(cellNamep, len);
748
749     /* add trailing backslash, if required */
750     tlen = (int)strlen(cellNamep);
751     if (tlen) {
752         if (cellNamep[tlen-1] != '\\') {
753             strncat(cellNamep, "\\", len);
754             cellNamep[len-1] = '\0';
755         }
756         
757         strncat(cellNamep, AFS_CELLSERVDB, len);
758         cellNamep[len-1] = '\0';
759     }
760     return 0;
761 }
762
763 /* look up the root cell's name in the Registry 
764  * Input buffer must be at least CELL_MAXNAMELEN 
765  * in size.  (Defined in cm_cell.h)
766  */
767 long cm_GetRootCellName(char *cellNamep)
768 {
769     DWORD code, dummyLen;
770     HKEY parmKey;
771
772     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
773                         0, KEY_QUERY_VALUE, &parmKey);
774     if (code != ERROR_SUCCESS)
775         return -1;
776
777     dummyLen = CELL_MAXNAMELEN;
778     code = RegQueryValueEx(parmKey, "Cell", NULL, NULL,
779                                 cellNamep, &dummyLen);
780     RegCloseKey (parmKey);
781     if (code != ERROR_SUCCESS || cellNamep[0] == 0)
782         return -1;
783
784     return 0;
785 }
786
787 cm_configFile_t *cm_CommonOpen(char *namep, char *rwp)
788 {
789     char wdir[MAX_PATH]="";
790     FILE *tfilep = NULL;
791
792     cm_GetConfigDir(wdir, sizeof(wdir));
793     if (*wdir) {
794         strncat(wdir, namep, sizeof(wdir));
795         wdir[sizeof(wdir)-1] = '\0';
796         
797         tfilep = fopen(wdir, rwp);
798     }
799     return ((cm_configFile_t *) tfilep);        
800 }       
801
802 long cm_WriteConfigString(char *labelp, char *valuep)
803 {
804     DWORD code, dummyDisp;
805     HKEY parmKey;
806
807     code = RegCreateKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
808                            0, "container", 0, KEY_SET_VALUE, NULL,
809                            &parmKey, &dummyDisp);
810     if (code != ERROR_SUCCESS)
811         return -1;
812
813     code = RegSetValueEx(parmKey, labelp, 0, REG_SZ,
814                           valuep, (DWORD)strlen(valuep) + 1);
815     RegCloseKey (parmKey);
816     if (code != ERROR_SUCCESS)
817         return (long)-1;
818
819     return (long)0;
820 }
821
822 long cm_WriteConfigInt(char *labelp, long value)
823 {
824     DWORD code, dummyDisp;
825     HKEY parmKey;
826
827     code = RegCreateKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
828                            0, "container", 0, KEY_SET_VALUE, NULL,
829                            &parmKey, &dummyDisp);
830     if (code != ERROR_SUCCESS)
831         return -1;
832
833     code = RegSetValueEx(parmKey, labelp, 0, REG_DWORD,
834                           (LPBYTE)&value, sizeof(value));
835     RegCloseKey (parmKey);
836     if (code != ERROR_SUCCESS)
837         return -1;
838
839     return 0;
840 }
841
842 cm_configFile_t *cm_OpenCellFile(void)
843 {
844     cm_configFile_t *cfp;
845
846     cfp = cm_CommonOpen(AFS_CELLSERVDB ".new", "w");
847     return cfp;
848 }
849
850 long cm_AppendPrunedCellList(cm_configFile_t *ofp, char *cellNamep)
851 {
852     cm_configFile_t *tfilep;    /* input file */
853     char *tp;
854     char lineBuffer[256];
855     char *valuep;
856     int inRightCell;
857     int foundCell;
858
859     tfilep = cm_CommonOpen(AFS_CELLSERVDB, "r");
860     if (!tfilep) 
861         return -1;
862
863     foundCell = 0;
864
865     /* have we seen the cell line for the guy we're looking for? */
866     inRightCell = 0;
867     while (1) {
868         tp = fgets(lineBuffer, sizeof(lineBuffer), (FILE *)tfilep);
869         if (tp == NULL) {
870             if (feof((FILE *)tfilep)) {
871                 /* hit EOF */
872                 fclose((FILE *)tfilep);
873                 return 0;
874             }
875         }
876
877         /* turn trailing cr or lf into null */
878         tp = strchr(lineBuffer, '\r');
879         if (tp) *tp = 0;
880         tp = strchr(lineBuffer, '\n');
881         if (tp) *tp = 0;
882                 
883         /* skip blank lines */
884         if (lineBuffer[0] == 0) {
885             fprintf((FILE *)ofp, "%s\n", lineBuffer);
886             continue;
887         }
888
889         if (lineBuffer[0] == '>') {
890             /* trim off at white space or '#' chars */
891             tp = strchr(lineBuffer, ' ');
892             if (tp) *tp = 0;
893             tp = strchr(lineBuffer, '\t');
894             if (tp) *tp = 0;
895             tp = strchr(lineBuffer, '#');
896             if (tp) *tp = 0;
897
898             /* now see if this is the right cell */
899             if (strcmp(lineBuffer+1, cellNamep) == 0) {
900                 /* found the cell we're looking for */
901                 inRightCell = 1;
902             }
903             else {
904                 inRightCell = 0;
905                 fprintf((FILE *)ofp, "%s\n", lineBuffer);
906             }
907         }
908         else {
909             valuep = strchr(lineBuffer, '#');
910             if (valuep == NULL) return -2;
911             valuep++;   /* skip the "#" */
912             if (!inRightCell) {
913                 fprintf((FILE *)ofp, "%s\n", lineBuffer);
914             }
915         }       /* a vldb line */
916     }           /* while loop processing all lines */
917 }       
918
919 long cm_AppendNewCell(cm_configFile_t *filep, char *cellNamep)
920 {
921     fprintf((FILE *)filep, ">%s\n", cellNamep);
922     return 0;
923 }
924
925 long cm_AppendNewCellLine(cm_configFile_t *filep, char *linep)
926 {
927     fprintf((FILE *)filep, "%s\n", linep);
928     return 0;
929 }
930
931 long cm_CloseCellFile(cm_configFile_t *filep)
932 {
933     char wdir[MAX_PATH];
934     char sdir[MAX_PATH];
935     long code;
936     long closeCode;
937     closeCode = fclose((FILE *)filep);
938
939     cm_GetConfigDir(wdir, sizeof(wdir));
940     strcpy(sdir, wdir);
941
942     if (closeCode != 0) {
943         /* something went wrong, preserve original database */
944         strncat(wdir, AFS_CELLSERVDB ".new", sizeof(wdir));
945         wdir[sizeof(wdir)-1] = '\0';
946         unlink(wdir);
947         return closeCode;
948     }
949
950     strncat(wdir, AFS_CELLSERVDB, sizeof(wdir));
951     wdir[sizeof(wdir)-1] = '\0';
952     strncat(sdir, AFS_CELLSERVDB ".new", sizeof(sdir));/* new file */
953     sdir[sizeof(sdir)-1] = '\0';
954
955     unlink(sdir);                       /* delete old file */
956
957     code = rename(sdir, wdir);  /* do the rename */
958
959     if (code) 
960         code = errno;
961
962     return code;
963 }   
964
965 void cm_GetConfigDir(char *dir, afs_uint32 len)
966 {
967     char * dirp = NULL;
968
969     if (!afssw_GetClientCellServDBDir(&dirp)) {
970         strncpy(dir, dirp, len);
971         dir[len-1] = '\0';
972         free(dirp);
973     } else {
974         dir[0] = '\0';
975     }
976 }