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