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