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