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