Permit DNS SRV record lookups to be used by the Windows afsconf_GetAfsdbInfo
[openafs.git] / src / auth / cellconfig.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 <afsconfig.h>
11 #include <afs/param.h>
12
13
14 #include <afs/stds.h>
15 #include <afs/pthread_glock.h>
16 #ifdef UKERNEL
17 #include "afs/sysincludes.h"
18 #include "afsincludes.h"
19 #include "des/des.h"
20 #include "rx/rxkad.h"
21 #include <netdb.h>
22 #include <ctype.h>
23 #else /* UKERNEL */
24 #include <sys/types.h>
25 #ifdef AFS_NT40_ENV
26 #include <winsock2.h>
27 #include <sys/utime.h>
28 #include <io.h>
29 #include <WINNT/afssw.h>
30 #else
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <sys/file.h>
35 #include <sys/time.h>
36 #include <ctype.h>
37 #ifdef AFS_AFSDB_ENV
38 #include <arpa/nameser.h>
39 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
40 #include <arpa/nameser_compat.h>
41 #endif
42 #include <resolv.h>
43 #endif /* AFS_AFSDB_ENV */
44 #endif /* AFS_NT40_ENV */
45 #include <afs/afsint.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <time.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include <string.h>
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #include <rx/rxkad.h>
58 #include <rx/rx.h>
59 #endif /* UKERNEL */
60 #include <afs/afsutil.h>
61 #include "cellconfig.h"
62 #include "keys.h"
63 #ifdef AFS_NT40_ENV
64 #ifdef AFS_AFSDB_ENV
65 #include <cm.h>
66 #include <cm_config.h>
67 /* cm_dns.h depends on cellconfig.h */
68 #include <cm_nls.h>
69 #include <cm_dns.h>
70 #endif /* AFS_AFSDB_ENV */
71 #endif
72 #include <rx/rx.h>
73 #include <rx/rxkad.h>
74
75 struct afsconf_servPair {
76     const char *name;
77     const char *ianaName;
78     int port;
79 };
80
81 static struct afsconf_servPair serviceTable[] = {
82     {"afs", "afs3-fileserver", 7000,},
83     {"afscb", "afs3-callback", 7001,},
84     {"afsprot", "afs3-prserver", 7002,},
85     {"afsvldb", "afs3-vlserver", 7003,},
86     {"afskauth", "afs3-kaserver", 7004,},
87     {"afsvol", "afs3-volserver", 7005,},
88     {"afserror", "afs3-errors", 7006,},
89     {"afsnanny", "afs3-bos", 7007,},
90     {"afsupdate", "afs3-update", 7008,},
91     {"afsrmtsys", "afs3-rmtsys", 7009,},
92     {"afsres", NULL, 7010,},/* residency database for MR-AFS */
93     {"afsremio", NULL, 7011,},  /* remote I/O interface for MR-AFS */
94     {0, 0, 0}                   /* insert new services before this spot */
95 };
96
97 /* Prototypes */
98 static int TrimLine(char *abuffer, int abufsize);
99 static int IsClientConfigDirectory(const char *path);
100 #ifdef AFS_NT40_ENV
101 static int GetCellNT(struct afsconf_dir *adir);
102 #endif
103 static int afsconf_Check(register struct afsconf_dir *adir);
104 static int afsconf_Touch(register struct afsconf_dir *adir);
105 static int GetCellUnix(struct afsconf_dir *adir);
106 static int afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
107                                 char clones[]);
108 static int ParseHostLine(char *aline, register struct sockaddr_in *addr,
109                          char *aname, char *aclone);
110 static int ParseCellLine(register char *aline, register char *aname,
111                          register char *alname);
112 static int afsconf_CloseInternal(register struct afsconf_dir *adir);
113 static int afsconf_Reopen(register struct afsconf_dir *adir);
114 static int SaveKeys(struct afsconf_dir *adir);
115
116 #ifndef T_AFSDB
117 #define T_AFSDB 18              /* per RFC1183 section 1 */
118 #endif
119
120 /*
121  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
122  * our code can tell if there is new info in the key files, the cell server db
123  * files or any of the other files (and reopen the thing) if the date on
124  * CellServDB changes.
125  */
126
127 #if defined(AFS_SUN5_ENV) && !defined(__sparcv9)
128 /* Solaris through 10 in 32 bit mode will return EMFILE if fopen can't
129    get an fd <= 255. We allow the fileserver to claim more fds than that.
130    This has always been a problem since pr_Initialize would have the same
131    issue, but hpr_Initialize makes it more likely that we would see this. 
132    Work around it. This is not generic. It's coded with the needs of
133    afsconf_* in mind only.
134
135    http://www.opensolaris.org/os/community/onnv/flag-days/pages/2006042001/
136 */
137
138 #define BUFFER 4096
139
140 struct afsconf_iobuffer {
141     int _file;
142     char *buffer;
143     char *ptr;
144     char *endptr;
145 };
146
147 typedef struct afsconf_iobuffer afsconf_FILE;
148
149 static afsconf_FILE *
150 afsconf_fopen(const char *fname, const char *fmode)
151 {
152     int fd;
153     afsconf_FILE *iop;
154     
155     if ((fd = open(fname, O_RDONLY)) == -1) {
156         return NULL;
157     }
158     
159     iop = malloc(sizeof(struct afsconf_iobuffer));
160     if (iop == NULL) {
161         (void) close(fd);
162         errno = ENOMEM;
163         return NULL;
164     }
165     iop->_file = fd;
166     iop->buffer = malloc(BUFFER);
167     if (iop->buffer == NULL) {
168         (void) close(fd);
169         free((void *) iop);
170         errno = ENOMEM;
171         return NULL;
172     }
173     iop->ptr = iop->buffer;
174     iop->endptr = iop->buffer;
175     return iop;
176 }
177
178 static int
179 afsconf_fclose(afsconf_FILE *iop)
180 {
181     if (iop == NULL) {
182         return 0;
183     }
184     close(iop->_file);
185     free((void *)iop->buffer);
186     free((void *)iop);
187     return 0;
188 }
189
190 static char *
191 afsconf_fgets(char *s, int n, afsconf_FILE *iop)
192 {
193     char *p;
194     
195     p = s;
196     for (;;) {
197         char c;
198         
199         if (iop->ptr == iop->endptr) {
200             ssize_t len;
201             
202             if ((len = read(iop->_file, (void *)iop->buffer, BUFFER)) == -1) {
203                 return NULL;
204             }
205             if (len == 0) {
206                 *p = 0;
207                 if (s == p) {
208                     return NULL;
209                 }
210                 return s;
211             }
212             iop->ptr = iop->buffer;
213             iop->endptr = iop->buffer + len;
214         }
215         c = *iop->ptr++;
216         *p++ = c;
217         if ((p - s) == (n - 1)) {
218             *p = 0;
219             return s;
220         }
221         if (c == '\n') {
222             *p = 0;
223             return s;
224         }
225     }
226 }
227 #define fopen afsconf_fopen
228 #define fclose afsconf_fclose
229 #define fgets afsconf_fgets
230 #else
231 #define afsconf_FILE FILE
232 #endif /* AFS_SUN5_ENV && ! __sparcv9 */
233
234 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
235 afs_int32
236 afsconf_FindService(const char *aname)
237 {
238     /* lookup a service name */
239     struct servent *ts;
240     struct afsconf_servPair *tsp;
241
242     if (aname == NULL || aname[0] == '\0')
243         return -1;
244
245 #if     defined(AFS_OSF_ENV) 
246     ts = getservbyname(aname, "");
247 #else
248     ts = (struct servent *) getservbyname(aname, NULL);
249 #endif
250     if (ts) {
251         /* we found it in /etc/services, so we use this value */
252         return ts->s_port;      /* already in network byte order */
253     }
254
255     /* not found in /etc/services, see if it is one of ours */
256     for (tsp = serviceTable; tsp->port; tsp++) {
257         if ((tsp->name && (!strcmp(tsp->name, aname)))
258             || (tsp->ianaName && (!strcmp(tsp->ianaName, aname))))
259             return htons(tsp->port);
260     }
261     return -1;
262 }
263
264 const char *
265 afsconf_FindIANAName(const char *aname)
266 {
267     /* lookup a service name */
268     struct afsconf_servPair *tsp;
269
270     if (aname == NULL || aname[0] == '\0')
271         return NULL;
272
273     /* see if it is one of ours */
274     for (tsp = serviceTable; tsp->port; tsp++) {
275         if ((tsp->name && (!strcmp(tsp->name, aname)))
276             || (tsp->ianaName && (!strcmp(tsp->ianaName, aname))))
277             return tsp->ianaName;
278     }
279     return NULL;
280 }
281
282 static int
283 TrimLine(char *abuffer, int abufsize)
284 {
285     char tbuffer[256];
286     register char *tp;
287     register int tc;
288
289     tp = abuffer;
290     while ((tc = *tp)) {
291         if (!isspace(tc))
292             break;
293         tp++;
294     }
295     strlcpy(tbuffer, tp, sizeof tbuffer);
296     strlcpy(abuffer, tbuffer, abufsize);
297     return 0;
298 }
299
300 /*
301  * IsClientConfigDirectory() -- determine if path matches well-known
302  *     client configuration directory.
303  */
304 #ifdef AFS_NT40_ENV
305 #define IS_SEP(x) ((x) == '\\' || (x) == '/')
306 #else /* AFS_NT40_ENV */
307 #define IS_SEP(x) ((x) == '/')
308 #endif /* AFS_NT40_ENV */
309 static int
310 IsClientConfigDirectory(const char *path)
311 {
312     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
313     int i, cc, pc;
314
315     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
316 #ifdef AFS_NT40_ENV
317         cc = tolower(cdir[i]);
318         pc = tolower(path[i]);
319
320         if (cc == '\\') {
321             cc = '/';
322         }
323         if (pc == '\\') {
324             pc = '/';
325         }
326 #else /* AFS_NT40_ENV */
327         cc = cdir[i];
328         pc = path[i];
329 #endif /* AFS_NT40_ENV */
330         if (cc != pc) {
331             return 0;
332         }
333     }
334
335     /* hit end of one or both; allow mismatch in existence of trailing slash */
336     if (cdir[i] != '\0') {
337         if (!IS_SEP(cdir[i]) || (cdir[i + 1] != '\0')) {
338             return 0;
339         }
340     }
341     if (path[i] != '\0') {
342         if (!IS_SEP(path[i]) || (path[i + 1] != '\0')) {
343             return 0;
344         }
345     }
346     return 1;
347 }
348
349
350 static int
351 afsconf_Check(register struct afsconf_dir *adir)
352 {
353     char tbuffer[256];
354 #ifdef AFS_NT40_ENV
355     char *p;
356 #endif
357     struct stat tstat;
358     register afs_int32 code;
359
360 #ifdef AFS_NT40_ENV
361     /* NT client CellServDB has different file name than NT server or Unix */
362     if (IsClientConfigDirectory(adir->name)) {
363         if (!afssw_GetClientCellServDBDir(&p)) {
364             strcompose(tbuffer, sizeof(tbuffer), p, "/",
365                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
366             free(p);
367         } else {
368             int len;
369             strncpy(tbuffer, adir->name, sizeof(tbuffer));
370             len = (int)strlen(tbuffer);
371             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
372                 strncat(tbuffer, "\\", sizeof(tbuffer));
373             }
374             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
375                     sizeof(tbuffer));
376             tbuffer[sizeof(tbuffer) - 1] = '\0';
377         }
378     } else {
379         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
380                    NULL);
381     }
382 #else
383     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
384 #endif /* AFS_NT40_ENV */
385
386     code = stat(tbuffer, &tstat);
387     if (code < 0) {
388         return code;
389     }
390     /* did file change? */
391     if (tstat.st_mtime == adir->timeRead) {
392         return 0;
393     }
394     /* otherwise file has changed, so reopen it */
395     return afsconf_Reopen(adir);
396 }
397
398 /* set modtime on file */
399 static int
400 afsconf_Touch(register struct afsconf_dir *adir)
401 {
402     char tbuffer[256];
403 #ifndef AFS_NT40_ENV
404     struct timeval tvp[2];
405 #else
406     char *p;
407 #endif
408
409     adir->timeRead = 0;         /* just in case */
410
411 #ifdef AFS_NT40_ENV
412     /* NT client CellServDB has different file name than NT server or Unix */
413
414     if (IsClientConfigDirectory(adir->name)) {
415         if (!afssw_GetClientCellServDBDir(&p)) {
416             strcompose(tbuffer, sizeof(tbuffer), p, "/",
417                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
418             free(p);
419         } else {
420             int len = (int)strlen(tbuffer);
421             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
422                 strncat(tbuffer, "\\", sizeof(tbuffer));
423             }
424             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
425                     sizeof(tbuffer));
426             tbuffer[sizeof(tbuffer) - 1] = '\0';
427         }
428     } else {
429         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
430                    NULL);
431     }
432
433     return _utime(tbuffer, NULL);
434
435 #else
436     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
437     gettimeofday(&tvp[0], NULL);
438     tvp[1] = tvp[0];
439     return utimes(tbuffer, tvp);
440 #endif /* AFS_NT40_ENV */
441 }
442
443 struct afsconf_dir *
444 afsconf_Open(register const char *adir)
445 {
446     register struct afsconf_dir *tdir;
447     register afs_int32 code;
448
449     LOCK_GLOBAL_MUTEX;
450     /* zero structure and fill in name; rest is done by internal routine */
451     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
452     memset(tdir, 0, sizeof(struct afsconf_dir));
453     tdir->name = strdup(adir);
454
455     code = afsconf_OpenInternal(tdir, 0, 0);
456     if (code) {
457         char *afsconf_path, afs_confdir[128];
458
459         free(tdir->name);
460         /* Check global place only when local Open failed for whatever reason */
461         if (!(afsconf_path = getenv("AFSCONF"))) {
462             /* The "AFSCONF" environment (or contents of "/.AFSCONF") will be typically set to something like "/afs/<cell>/common/etc" where, by convention, the default files for "ThisCell" and "CellServDB" will reside; note that a major drawback is that a given afs client on that cell may NOT contain the same contents... */
463             char *home_dir;
464             afsconf_FILE *fp;
465             size_t len;
466
467             if (!(home_dir = getenv("HOME"))) {
468                 /* Our last chance is the "/.AFSCONF" file */
469                 fp = fopen("/.AFSCONF", "r");
470                 if (fp == 0) {
471                     free(tdir);
472                     UNLOCK_GLOBAL_MUTEX;
473                     return (struct afsconf_dir *)0;
474                 }
475                 fgets(afs_confdir, 128, fp);
476                 fclose(fp);
477             } else {
478                 char pathname[256];
479
480                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
481                 fp = fopen(pathname, "r");
482                 if (fp == 0) {
483                     /* Our last chance is the "/.AFSCONF" file */
484                     fp = fopen("/.AFSCONF", "r");
485                     if (fp == 0) {
486                         free(tdir);
487                         UNLOCK_GLOBAL_MUTEX;
488                         return (struct afsconf_dir *)0;
489                     }
490                 }
491                 fgets(afs_confdir, 128, fp);
492                 fclose(fp);
493             }
494             len = strlen(afs_confdir);
495             if (len == 0) {
496                 free(tdir);
497                 UNLOCK_GLOBAL_MUTEX;
498                 return (struct afsconf_dir *)0;
499             }
500             if (afs_confdir[len - 1] == '\n') {
501                 afs_confdir[len - 1] = 0;
502             }
503             afsconf_path = afs_confdir;
504         }
505         tdir->name = strdup(afsconf_path);
506         code = afsconf_OpenInternal(tdir, 0, 0);
507         if (code) {
508             free(tdir->name);
509             free(tdir);
510             UNLOCK_GLOBAL_MUTEX;
511             return (struct afsconf_dir *)0;
512         }
513     }
514     UNLOCK_GLOBAL_MUTEX;
515     return tdir;
516 }
517
518 static int
519 GetCellUnix(struct afsconf_dir *adir)
520 {
521     char *rc;
522     char tbuffer[256];
523     char *start, *p;
524     afsconf_FILE *fp;
525     
526     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
527     fp = fopen(tbuffer, "r");
528     if (fp == 0) {
529         return -1;
530     }
531     rc = fgets(tbuffer, 256, fp);
532     fclose(fp);
533     if (rc == NULL)
534         return -1;
535
536     start = tbuffer;
537     while (*start != '\0' && isspace(*start))
538         start++;
539     p = start;
540     while (*p != '\0' && !isspace(*p))
541         p++;
542     *p = '\0';
543     if (*start == '\0')
544         return -1;
545
546     adir->cellName = strdup(start);
547     return 0;
548 }
549
550
551 #ifdef AFS_NT40_ENV
552 static int
553 GetCellNT(struct afsconf_dir *adir)
554 {
555     if (IsClientConfigDirectory(adir->name)) {
556         /* NT client config dir; ThisCell is in registry (no file). */
557         return afssw_GetClientCellName(&adir->cellName);
558     } else {
559         /* NT server config dir; works just like Unix */
560         return GetCellUnix(adir);
561     }
562 }
563
564 /* The following procedures and structs are used on Windows only
565  * to enumerate the Cell information distributed within the 
566  * Windows registry.  (See src/WINNT/afsd/cm_config.c)
567  */
568 typedef struct _cm_enumCellRegistry {
569     afs_uint32 client;  /* non-zero if client query */
570     struct afsconf_dir *adir;
571 } cm_enumCellRegistry_t;
572
573 static long
574 cm_serverConfigProc(void *rockp, struct sockaddr_in *addrp, 
575                     char *hostNamep, unsigned short rank)
576 {
577     struct afsconf_cell *cellInfop = (struct afsconf_cell *)rockp;
578
579     if (cellInfop->numServers == MAXHOSTSPERCELL)
580         return 0;
581
582     cellInfop->hostAddr[cellInfop->numServers] = *addrp;
583     strncpy(cellInfop->hostName[cellInfop->numServers], hostNamep, MAXHOSTCHARS);
584     cellInfop->hostName[cellInfop->numServers][MAXHOSTCHARS-1] = '\0';
585     cellInfop->numServers++;
586
587     return 0;
588 }
589
590 static long
591 cm_enumCellRegistryProc(void *rockp, char * cellNamep)
592 {
593     long code;
594     cm_enumCellRegistry_t *enump = (cm_enumCellRegistry_t *)rockp;
595     char linkedName[256] = "";
596     int timeout = 0;
597     struct afsconf_entry *newEntry;
598
599
600     newEntry = malloc(sizeof(struct afsconf_entry));
601     if (newEntry == NULL)
602         return ENOMEM;
603     newEntry->cellInfo.numServers = 0;
604
605     code = cm_SearchCellRegistry(enump->client, cellNamep, NULL, linkedName, cm_serverConfigProc, &newEntry->cellInfo);
606     if (code == CM_ERROR_FORCE_DNS_LOOKUP)
607         code = cm_SearchCellByDNS(cellNamep, NULL, &timeout, cm_serverConfigProc, &newEntry->cellInfo);
608
609     if (code == 0) {
610         strncpy(newEntry->cellInfo.name, cellNamep, MAXCELLCHARS);
611         newEntry->cellInfo.name[MAXCELLCHARS-1];
612         if (linkedName[0])
613             newEntry->cellInfo.linkedCell = strdup(linkedName);
614         else
615             newEntry->cellInfo.linkedCell = NULL;
616         newEntry->cellInfo.timeout = timeout;
617         newEntry->cellInfo.flags = 0;
618
619         newEntry->next = enump->adir->entries;
620         enump->adir->entries = newEntry;
621     } else {
622         free(newEntry);
623     }
624     return code;
625 }       
626 #endif /* AFS_NT40_ENV */
627
628
629 static int
630 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
631                      char clones[])
632 {
633     afsconf_FILE *tf;
634     register char *tp, *bp;
635     register struct afsconf_entry *curEntry;
636     struct afsconf_aliasentry *curAlias;
637     register afs_int32 code;
638     afs_int32 i;
639     char tbuffer[256], tbuf1[256];
640     struct stat tstat;
641 #ifdef AFS_NT40_ENV
642     cm_enumCellRegistry_t enumCellRegistry = {0, 0};
643 #endif /* AFS_NT40_ENV */
644
645     /* figure out the local cell name */
646 #ifdef AFS_NT40_ENV
647     i = GetCellNT(adir);
648     enumCellRegistry.adir = adir;
649 #else
650     i = GetCellUnix(adir);
651 #endif
652
653 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
654     if (i) {
655         return i;
656     }
657 #endif
658
659     /* now parse the individual lines */
660     curEntry = 0;
661
662 #ifdef AFS_NT40_ENV
663     /* NT client/server have a CellServDB that is the same format as Unix.
664      * However, the NT client uses a different file name
665      */
666     if (IsClientConfigDirectory(adir->name)) {
667         /* NT client config dir */
668         char *p;
669
670         enumCellRegistry.client = 1;
671
672         if (!afssw_GetClientCellServDBDir(&p)) {
673             strcompose(tbuffer, sizeof(tbuffer), p, "/",
674                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
675             free(p);
676         } else {
677             int len;
678             strncpy(tbuffer, adir->name, sizeof(tbuffer));
679             len = (int)strlen(tbuffer);
680             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
681                 strncat(tbuffer, "\\", sizeof(tbuffer));
682             }
683             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
684                     sizeof(tbuffer));
685             tbuffer[sizeof(tbuffer) - 1] = '\0';
686         }
687     } else {
688         /* NT server config dir */
689         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
690                    NULL);
691     }
692 #else
693     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
694 #endif /* AFS_NT40_ENV */
695
696     if (!stat(tbuffer, &tstat)) {
697         adir->timeRead = tstat.st_mtime;
698     } else {
699         adir->timeRead = 0;
700     }
701
702     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
703     tf = fopen(tbuffer, "r");
704     if (!tf) {
705         return -1;
706     }
707
708     /* The CellServDB file is now open.  
709      * The following code parses the contents of the 
710      * file and creates a list with the first cell entry
711      * in the CellServDB file at the end of the list.
712      * 
713      * No checking is performed for duplicates.
714      * The side effects of this process are that duplicate
715      * entries appended to the end of the CellServDB file
716      * take precedence and are found in a shorter period 
717      * of time.
718      */
719
720     while (1) {
721         tp = fgets(tbuffer, sizeof(tbuffer), tf);
722         if (!tp)
723             break;
724         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
725         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
726             continue;           /* empty line */
727         if (tbuffer[0] == '>') {
728             char linkedcell[MAXCELLCHARS];
729             /* start new cell item */
730             if (curEntry) {
731                 /* thread this guy on the list */
732                 curEntry->next = adir->entries;
733                 adir->entries = curEntry;
734                 curEntry = 0;
735             }
736             curEntry =
737                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
738             memset(curEntry, 0, sizeof(struct afsconf_entry));
739             code =
740                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
741             if (code) {
742                 afsconf_CloseInternal(adir);
743                 fclose(tf);
744                 free(curEntry);
745                 return -1;
746             }
747             if (linkedcell[0] != '\0')
748                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
749         } else {
750             /* new host in the current cell */
751             if (!curEntry) {
752                 afsconf_CloseInternal(adir);
753                 fclose(tf);
754                 return -1;
755             }
756             i = curEntry->cellInfo.numServers;
757             if (i < MAXHOSTSPERCELL) {
758                 if (cell && !strcmp(cell, curEntry->cellInfo.name))
759                     code =
760                         ParseHostLine(tbuffer, 
761                                       &curEntry->cellInfo.hostAddr[i],
762                                       curEntry->cellInfo.hostName[i], 
763                                       &clones[i]);
764                 else
765                     code =
766                         ParseHostLine(tbuffer, 
767                                       &curEntry->cellInfo.hostAddr[i],
768                                       curEntry->cellInfo.hostName[i], 0);
769
770                 if (code) {
771                     if (code == AFSCONF_SYNTAX) {
772                         for (bp = tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
773                             if (!*bp)
774                                 break;
775                         }
776                         *bp = '\0';
777                         fprintf(stderr,
778                                 "Can't properly parse host line \"%s\" in configuration file %s\n",
779                                 tbuffer, tbuf1);
780                     }
781                     free(curEntry);
782                     fclose(tf);
783                     afsconf_CloseInternal(adir);
784                     return -1;
785                 }
786                 curEntry->cellInfo.numServers = ++i;
787             } else {
788                 fprintf(stderr,
789                         "Too many hosts for cell %s in configuration file %s\n", 
790                         curEntry->cellInfo.name, tbuf1);
791             }
792         }
793     }
794     fclose(tf);                 /* close the file now */
795
796     /* end the last partially-completed cell */
797     if (curEntry) {
798         curEntry->next = adir->entries;
799         adir->entries = curEntry;
800     }
801
802 #ifdef AFS_NT40_ENV
803      /* 
804       * Windows maintains a CellServDB list in the Registry
805       * that supercedes the contents of the CellServDB file.
806       * Prepending these entries to the head of the list 
807       * is sufficient to enforce the precedence.
808       */
809      cm_EnumerateCellRegistry( enumCellRegistry.client,
810                                cm_enumCellRegistryProc,
811                                &enumCellRegistry);
812 #endif /* AFS_NT40_ENV */
813
814     /* Read in the alias list */
815     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
816
817     tf = fopen(tbuffer, "r");
818     while (tf) {
819         char *aliasPtr;
820
821         tp = fgets(tbuffer, sizeof(tbuffer), tf);
822         if (!tp)
823             break;
824         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
825
826         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
827             continue;           /* empty line */
828
829         tp = tbuffer;
830         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
831             tp++;
832         if (tp[0] == '\0')
833             continue;           /* invalid line */
834
835         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
836             0[tp++] = '\0';
837         if (tp[0] == '\0')
838             continue;           /* invalid line */
839
840         aliasPtr = tp;
841         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
842                && tp[0] != '\n')
843             tp++;
844         tp[0] = '\0';
845
846         curAlias = malloc(sizeof(*curAlias));
847         memset(curAlias, 0, sizeof(*curAlias));
848
849         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
850         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
851
852         curAlias->next = adir->alias_entries;
853         adir->alias_entries = curAlias;
854     }
855
856     if (tf != NULL)
857         fclose(tf);
858     /* now read the fs keys, if possible */
859     adir->keystr = (struct afsconf_keys *)0;
860     afsconf_IntGetKeys(adir);
861
862     return 0;
863 }
864
865 /* parse a line of the form
866  *"128.2.1.3   #hostname" or
867  *"[128.2.1.3]  #hostname" for clones
868  * into the appropriate pieces.  
869  */
870 static int
871 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
872               char *aclone)
873 {
874     int c1, c2, c3, c4;
875     register afs_int32 code;
876     register char *tp;
877
878     if (*aline == '[') {
879         if (aclone)
880             *aclone = 1;
881         /* FIXME: length of aname unknown here */
882         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
883     } else {
884         if (aclone)
885             *aclone = 0;
886         /* FIXME: length of aname unknown here */
887         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
888     }
889     if (code != 5)
890         return AFSCONF_SYNTAX;
891     addr->sin_family = AF_INET;
892     addr->sin_port = 0;
893 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
894     addr->sin_len = sizeof(struct sockaddr_in);
895 #endif
896     tp = (char *)&addr->sin_addr;
897     *tp++ = c1;
898     *tp++ = c2;
899     *tp++ = c3;
900     *tp++ = c4;
901     return 0;
902 }
903
904 /* parse a line of the form
905  * ">cellname [linkedcellname] [#comments]"
906  * into the appropriate pieces.
907  */
908 static int
909 ParseCellLine(register char *aline, register char *aname,
910               register char *alname)
911 {
912     register int code;
913     /* FIXME: length of aname, alname unknown here */
914     code = sscanf(aline, ">%s %s", aname, alname);
915     if (code == 1)
916         *alname = '\0';
917     if (code == 2) {
918         if (*alname == '#') {
919             *alname = '\0';
920         }
921     }
922     return (code > 0 ? 0 : AFSCONF_SYNTAX);
923 }
924
925 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
926 int
927 afsconf_CellApply(struct afsconf_dir *adir,
928                   int (*aproc) (struct afsconf_cell * cell, void *arock,
929                                 struct afsconf_dir * dir), void *arock)
930 {
931     register struct afsconf_entry *tde;
932     register afs_int32 code;
933     LOCK_GLOBAL_MUTEX;
934     for (tde = adir->entries; tde; tde = tde->next) {
935         code = (*aproc) (&tde->cellInfo, arock, adir);
936         if (code) {
937             UNLOCK_GLOBAL_MUTEX;
938             return code;
939         }
940     }
941     UNLOCK_GLOBAL_MUTEX;
942     return 0;
943 }
944
945 /* call aproc(entry, arock, adir) for all cell aliases.
946  * Proc must return 0, or we'll stop early and return the code it returns
947  */
948 int
949 afsconf_CellAliasApply(struct afsconf_dir *adir,
950                        int (*aproc) (struct afsconf_cellalias * alias,
951                                      void *arock, struct afsconf_dir * dir),
952                        void *arock)
953 {
954     register struct afsconf_aliasentry *tde;
955     register afs_int32 code;
956     LOCK_GLOBAL_MUTEX;
957     for (tde = adir->alias_entries; tde; tde = tde->next) {
958         code = (*aproc) (&tde->aliasInfo, arock, adir);
959         if (code) {
960             UNLOCK_GLOBAL_MUTEX;
961             return code;
962         }
963     }
964     UNLOCK_GLOBAL_MUTEX;
965     return 0;
966 }
967
968 afs_int32 afsconf_SawCell = 0;
969
970 int
971 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
972                             char *aservice, struct afsconf_cell *acellInfo,
973                             char clones[])
974 {
975     afs_int32 code;
976     char *cell;
977
978     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
979     if (code)
980         return code;
981
982     if (acellName)
983         cell = acellName;
984     else
985         cell = (char *)&acellInfo->name;
986
987     code = afsconf_OpenInternal(adir, cell, clones);
988     return code;
989 }
990
991 #ifdef AFS_AFSDB_ENV
992 #if !defined(AFS_NT40_ENV)
993 int
994 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
995                      struct afsconf_cell *acellInfo)
996 {
997     afs_int32 code;
998     int tservice, i, len;
999     unsigned char answer[1024];
1000     unsigned char *p;
1001     char *dotcellname;
1002     int cellnamelength;
1003     char realCellName[256];
1004     char host[256];
1005     int server_num = 0;
1006     int minttl = 0;
1007     int try_init = 0;
1008
1009     /* The resolver isn't always MT-safe.. Perhaps this ought to be
1010      * replaced with a more fine-grained lock just for the resolver
1011      * operations.
1012      */
1013
1014  retryafsdb:
1015     if ( ! strchr(acellName,'.') ) {
1016        cellnamelength=strlen(acellName);
1017        dotcellname=malloc(cellnamelength+2);
1018        memcpy(dotcellname,acellName,cellnamelength);
1019        dotcellname[cellnamelength]='.';
1020        dotcellname[cellnamelength+1]=0;
1021        LOCK_GLOBAL_MUTEX;
1022        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
1023        if ( len < 0 ) {
1024           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
1025        }
1026        UNLOCK_GLOBAL_MUTEX;
1027        free(dotcellname);
1028     } else {
1029        LOCK_GLOBAL_MUTEX;
1030        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
1031        UNLOCK_GLOBAL_MUTEX;
1032     }
1033     if (len < 0) {
1034         if (try_init < 1) {
1035             try_init++;
1036             res_init();
1037             goto retryafsdb;
1038         }
1039         return AFSCONF_NOTFOUND;
1040     }
1041
1042     p = answer + sizeof(HEADER);        /* Skip header */
1043     code = dn_expand(answer, answer + len, p, host, sizeof(host));
1044     if (code < 0)
1045         return AFSCONF_NOTFOUND;
1046
1047     p += code + QFIXEDSZ;       /* Skip name */
1048
1049     while (p < answer + len) {
1050         int type, ttl, size;
1051
1052         code = dn_expand(answer, answer + len, p, host, sizeof(host));
1053         if (code < 0)
1054             return AFSCONF_NOTFOUND;
1055
1056         p += code;              /* Skip the name */
1057         type = (p[0] << 8) | p[1];
1058         p += 4;                 /* Skip type and class */
1059         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
1060         p += 4;                 /* Skip the TTL */
1061         size = (p[0] << 8) | p[1];
1062         p += 2;                 /* Skip the size */
1063
1064         if (type == T_AFSDB) {
1065             struct hostent *he;
1066             short afsdb_type;
1067
1068             afsdb_type = (p[0] << 8) | p[1];
1069             if (afsdb_type == 1) {
1070                 /*
1071                  * We know this is an AFSDB record for our cell, of the
1072                  * right AFSDB type.  Write down the true cell name that
1073                  * the resolver gave us above.
1074                  */
1075                 strlcpy(realCellName, host, sizeof realCellName);
1076             }
1077
1078             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
1079             if (code < 0)
1080                 return AFSCONF_NOTFOUND;
1081
1082             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
1083                 /* Do we want to get TTL data for the A record as well? */
1084                 (he = gethostbyname(host))) {
1085                 afs_int32 ipaddr;
1086                 memcpy(&ipaddr, he->h_addr, he->h_length);
1087                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
1088                 strncpy(acellInfo->hostName[server_num], host,
1089                         sizeof(acellInfo->hostName[server_num]));
1090                 server_num++;
1091
1092                 if (!minttl || ttl < minttl)
1093                     minttl = ttl;
1094             }
1095         }
1096
1097         p += size;
1098     }
1099
1100     if (server_num == 0)        /* No AFSDB records */
1101         return AFSCONF_NOTFOUND;
1102
1103     /* Convert the real cell name to lowercase */
1104     for (p = (unsigned char *)realCellName; *p; p++)
1105         *p = tolower(*p);
1106
1107     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
1108     acellInfo->numServers = server_num;
1109
1110     if (aservice) {
1111         tservice = afsconf_FindService(aservice);
1112         if (tservice < 0)
1113             return AFSCONF_NOTFOUND;    /* service not found */
1114         for (i = 0; i < acellInfo->numServers; i++) {
1115             acellInfo->hostAddr[i].sin_port = tservice;
1116         }
1117     }
1118
1119     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
1120
1121     return 0;
1122 }
1123 #else /* windows */
1124 int
1125 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1126                      struct afsconf_cell *acellInfo)
1127 {
1128     afs_int32 i;
1129     int tservice = afsconf_FindService(aservice);
1130     const char *ianaName = afsconf_FindIANAName(aservice);
1131     struct afsconf_entry DNSce;
1132     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1133     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1134     unsigned short ipRanks[AFSMAXCELLHOSTS];
1135     unsigned short ports[AFSMAXCELLHOSTS];
1136     int numServers;
1137     int rc;
1138     int ttl;
1139
1140     if (tservice < 0) {
1141         if (aservice)
1142             return AFSCONF_NOTFOUND;
1143         else
1144             tservice = 0;       /* port will be assigned by caller */
1145     }
1146
1147     if (ianaName == NULL)
1148         ianaName = "afs3-vlserver";
1149
1150     DNSce.cellInfo.numServers = 0;
1151     DNSce.next = NULL;
1152
1153     rc = getAFSServer(ianaName, "udp", acellName, tservice,
1154                       cellHostAddrs, cellHostNames, ports, ipRanks, &numServers,
1155                       &ttl);
1156     /* ignore the ttl here since this code is only called by transitory programs
1157      * like klog, etc. */
1158
1159     /* If we couldn't find an entry for the requested service
1160      * and that service happens to be the prservice or kaservice
1161      * then fallback to searching for afs3-vlserver and assigning
1162      * the port number here. */
1163     if (rc < 0 && tservice == 7002 || tservice == 7004) {
1164         rc = getAFSServer("afs3-vlserver", "udp", acellName, tservice,
1165                            cellHostAddrs, cellHostNames, ports, ipRanks, &numServers,
1166                            &ttl);
1167         if (rc >= 0) {
1168             for (i = 0; i < numServers; i++)
1169                 ports[i] = tservice;
1170         }
1171     }
1172
1173     if (rc < 0 || numServers == 0)
1174         return -1;
1175
1176     for (i = 0; i < numServers; i++) {
1177         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1178                sizeof(long));
1179         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1180         acellInfo->hostAddr[i].sin_family = AF_INET;
1181         if (aservice)
1182             acellInfo->hostAddr[i].sin_port = ports[i];
1183         else
1184             acellInfo->hostAddr[i].sin_port = 0;
1185     }
1186
1187     acellInfo->numServers = numServers;
1188     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1189     acellInfo->linkedCell = NULL;       /* no linked cell */
1190     acellInfo->flags = 0;
1191     return 0;
1192 }
1193 #endif /* windows */
1194 #endif /* AFS_AFSDB_ENV */
1195
1196 int
1197 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1198                     struct afsconf_cell *acellInfo)
1199 {
1200     register struct afsconf_entry *tce;
1201     struct afsconf_aliasentry *tcae;
1202     struct afsconf_entry *bestce;
1203     register afs_int32 i;
1204     int tservice;
1205     char *tcell;
1206     int cnLen;
1207     int ambig;
1208     char tbuffer[64];
1209
1210     LOCK_GLOBAL_MUTEX;
1211     if (adir)
1212         afsconf_Check(adir);
1213     if (acellName) {
1214         tcell = acellName;
1215         cnLen = (int)(strlen(tcell) + 1);
1216         lcstring(tcell, tcell, cnLen);
1217         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1218         /* call to afsconf_GetLocalCell: like klog  */
1219     } else {
1220         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1221         if (i) {
1222             UNLOCK_GLOBAL_MUTEX;
1223             return i;
1224         }
1225         tcell = tbuffer;
1226     }
1227     cnLen = strlen(tcell);
1228     bestce = (struct afsconf_entry *)0;
1229     ambig = 0;
1230     if (!adir) {
1231         UNLOCK_GLOBAL_MUTEX;
1232         return 0;
1233     }
1234
1235     /* Look through the list of aliases */
1236     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1237         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1238             tcell = tcae->aliasInfo.realName;
1239             break;
1240         }
1241     }
1242
1243     for (tce = adir->entries; tce; tce = tce->next) {
1244         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1245             /* found our cell */
1246             bestce = tce;
1247             ambig = 0;
1248             break;
1249         }
1250         if (strlen(tce->cellInfo.name) < cnLen)
1251             continue;           /* clearly wrong */
1252         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1253             if (bestce)
1254                 ambig = 1;      /* ambiguous unless we get exact match */
1255             bestce = tce;
1256         }
1257     }
1258     if (!ambig && bestce && bestce->cellInfo.numServers) {
1259         *acellInfo = bestce->cellInfo;  /* structure assignment */
1260         if (aservice) {
1261             tservice = afsconf_FindService(aservice);
1262             if (tservice < 0) {
1263                 UNLOCK_GLOBAL_MUTEX;
1264                 return AFSCONF_NOTFOUND;        /* service not found */
1265             }
1266             for (i = 0; i < acellInfo->numServers; i++) {
1267                 acellInfo->hostAddr[i].sin_port = tservice;
1268             }
1269         }
1270         acellInfo->timeout = 0;
1271
1272         /* 
1273          * Until we figure out how to separate out ubik server
1274          * queries from other server queries, only perform gethostbyname()
1275          * lookup on the specified hostnames for the client CellServDB files.
1276          */
1277         if (IsClientConfigDirectory(adir->name) && 
1278             !(acellInfo->flags & AFSCONF_CELL_FLAG_DNS_QUERIED)) {
1279             int j;
1280             short numServers=0;                                 /*Num active servers for the cell */
1281             struct sockaddr_in hostAddr[MAXHOSTSPERCELL];       /*IP addresses for cell's servers */
1282             char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS];       /*Names for cell's servers */
1283
1284             memset(&hostAddr, 0, sizeof(hostAddr));
1285             memset(&hostName, 0, sizeof(hostName));
1286
1287             for ( j=0; j<acellInfo->numServers && numServers < MAXHOSTSPERCELL; j++ ) {
1288                 struct hostent *he = gethostbyname(acellInfo->hostName[j]);
1289                 int foundAddr = 0;
1290
1291                 if (he && he->h_addrtype == AF_INET) {
1292                     int i;
1293                     /* obtain all the valid address from the list */
1294                     for (i=0 ; he->h_addr_list[i] && numServers < MAXHOSTSPERCELL; i++) {
1295                         /* check to see if this is a new address; if so insert it into the list */
1296                         int k, dup;
1297                         for (k=0, dup=0; !dup && k < numServers; k++) {
1298                             if (hostAddr[k].sin_addr.s_addr == *(u_long *)he->h_addr_list[i])
1299                                 dup = 1;
1300                         }
1301                         if (dup)
1302                             continue;
1303
1304                         hostAddr[numServers].sin_family = AF_INET;
1305                         hostAddr[numServers].sin_port = acellInfo->hostAddr[0].sin_port;
1306 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
1307                         hostAddr[numServers].sin_len = sizeof(struct sockaddr_in);
1308 #endif
1309                         memcpy(&hostAddr[numServers].sin_addr.s_addr, he->h_addr_list[i], sizeof(long));
1310                         strcpy(hostName[numServers], acellInfo->hostName[j]);
1311                         foundAddr = 1;
1312                         numServers++;
1313                     }
1314                 }
1315                 if (!foundAddr) {
1316                     hostAddr[numServers] = acellInfo->hostAddr[j];
1317                     strcpy(hostName[numServers], acellInfo->hostName[j]);
1318                     numServers++;
1319                 }
1320             }
1321
1322             for (i=0; i<numServers; i++) {
1323                 acellInfo->hostAddr[i] = hostAddr[i];
1324                 strcpy(acellInfo->hostName[i], hostName[i]);
1325             }
1326             acellInfo->numServers = numServers;
1327             acellInfo->flags |= AFSCONF_CELL_FLAG_DNS_QUERIED;
1328         }
1329         UNLOCK_GLOBAL_MUTEX;
1330         return 0;
1331     } else {
1332         UNLOCK_GLOBAL_MUTEX;
1333 #ifdef AFS_AFSDB_ENV
1334         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1335 #else
1336         return AFSCONF_NOTFOUND;
1337 #endif /* AFS_AFSDB_ENV */
1338     }
1339 }
1340
1341 int
1342 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1343                      afs_int32 alen)
1344 {
1345     static int afsconf_showcell = 0;
1346     char *afscell_path;
1347     afs_int32 code = 0;
1348
1349     LOCK_GLOBAL_MUTEX;
1350     /*
1351      * If a cell switch was specified in a command, then it should override the 
1352      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1353      * is set and the cell name in the adir structure is used.
1354      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1355      */
1356     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1357         if (!afsconf_showcell) {
1358             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1359                     afscell_path);
1360             afsconf_showcell = 1;
1361         }
1362         strncpy(aname, afscell_path, alen);
1363     } else {
1364         afsconf_Check(adir);
1365         if (adir->cellName) {
1366             strncpy(aname, adir->cellName, alen);
1367         } else
1368             code = AFSCONF_UNKNOWN;
1369     }
1370
1371     UNLOCK_GLOBAL_MUTEX;
1372     return (code);
1373 }
1374
1375 int
1376 afsconf_Close(struct afsconf_dir *adir)
1377 {
1378     LOCK_GLOBAL_MUTEX;
1379     afsconf_CloseInternal(adir);
1380     if (adir->name)
1381         free(adir->name);
1382     free(adir);
1383     UNLOCK_GLOBAL_MUTEX;
1384     return 0;
1385 }
1386
1387 static int
1388 afsconf_CloseInternal(register struct afsconf_dir *adir)
1389 {
1390     register struct afsconf_entry *td, *nd;
1391     struct afsconf_aliasentry *ta, *na;
1392     register char *tname;
1393
1394     tname = adir->name;         /* remember name, since that's all we preserve */
1395
1396     /* free everything we can find */
1397     if (adir->cellName)
1398         free(adir->cellName);
1399     for (td = adir->entries; td; td = nd) {
1400         nd = td->next;
1401         if (td->cellInfo.linkedCell)
1402             free(td->cellInfo.linkedCell);
1403         free(td);
1404     }
1405     for (ta = adir->alias_entries; ta; ta = na) {
1406         na = ta->next;
1407         free(ta);
1408     }
1409     if (adir->keystr)
1410         free(adir->keystr);
1411
1412     /* reinit */
1413     memset(adir, 0, sizeof(struct afsconf_dir));
1414     adir->name = tname;         /* restore it */
1415     return 0;
1416 }
1417
1418 static int
1419 afsconf_Reopen(register struct afsconf_dir *adir)
1420 {
1421     register afs_int32 code;
1422     code = afsconf_CloseInternal(adir);
1423     if (code)
1424         return code;
1425     code = afsconf_OpenInternal(adir, 0, 0);
1426     return code;
1427 }
1428
1429 /* called during opening of config file */
1430 int
1431 afsconf_IntGetKeys(struct afsconf_dir *adir)
1432 {
1433     char tbuffer[256];
1434     register int fd;
1435     struct afsconf_keys *tstr;
1436     register afs_int32 code;
1437
1438 #ifdef AFS_NT40_ENV
1439     /* NT client config dir has no KeyFile; don't risk attempting open
1440      * because there might be a random file of this name if dir is shared.
1441      */
1442     if (IsClientConfigDirectory(adir->name)) {
1443         adir->keystr = ((struct afsconf_keys *)
1444                         malloc(sizeof(struct afsconf_keys)));
1445         adir->keystr->nkeys = 0;
1446         return 0;
1447     }
1448 #endif /* AFS_NT40_ENV */
1449
1450     LOCK_GLOBAL_MUTEX;
1451     /* compute the key name and other setup */
1452     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1453     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1454     adir->keystr = tstr;
1455
1456     /* read key file */
1457     fd = open(tbuffer, O_RDONLY);
1458     if (fd < 0) {
1459         tstr->nkeys = 0;
1460         UNLOCK_GLOBAL_MUTEX;
1461         return 0;
1462     }
1463     code = read(fd, tstr, sizeof(struct afsconf_keys));
1464     close(fd);
1465     if (code < sizeof(afs_int32)) {
1466         tstr->nkeys = 0;
1467         UNLOCK_GLOBAL_MUTEX;
1468         return 0;
1469     }
1470
1471     /* convert key structure to host order */
1472     tstr->nkeys = ntohl(tstr->nkeys);
1473
1474     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1475         tstr->nkeys = 0;
1476         UNLOCK_GLOBAL_MUTEX;
1477         return 0;
1478     }
1479
1480     for (fd = 0; fd < tstr->nkeys; fd++)
1481         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1482
1483     UNLOCK_GLOBAL_MUTEX;
1484     return 0;
1485 }
1486
1487 /* get keys structure */
1488 int
1489 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1490 {
1491     register afs_int32 code;
1492
1493     LOCK_GLOBAL_MUTEX;
1494     code = afsconf_Check(adir);
1495     if (code) {
1496         UNLOCK_GLOBAL_MUTEX;
1497         return AFSCONF_FAILURE;
1498     }
1499     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1500     UNLOCK_GLOBAL_MUTEX;
1501     return 0;
1502 }
1503
1504 /* get latest key */
1505 afs_int32
1506 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1507                      struct ktc_encryptionKey *akey)
1508 {
1509     register int i;
1510     int maxa;
1511     register struct afsconf_key *tk;
1512     register afs_int32 best;
1513     struct afsconf_key *bestk;
1514     register afs_int32 code;
1515
1516     LOCK_GLOBAL_MUTEX;
1517     code = afsconf_Check(adir);
1518     if (code) {
1519         UNLOCK_GLOBAL_MUTEX;
1520         return AFSCONF_FAILURE;
1521     }
1522     maxa = adir->keystr->nkeys;
1523
1524     best = -1;                  /* highest kvno we've seen yet */
1525     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1526     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1527         if (tk->kvno == 999)
1528             continue;           /* skip bcrypt keys */
1529         if (tk->kvno > best) {
1530             best = tk->kvno;
1531             bestk = tk;
1532         }
1533     }
1534     if (bestk) {                /* found any  */
1535         if (akey)
1536             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1537         if (avno)
1538             *avno = bestk->kvno;        /* and kvno to caller */
1539         UNLOCK_GLOBAL_MUTEX;
1540         return 0;
1541     }
1542     UNLOCK_GLOBAL_MUTEX;
1543     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1544 }
1545
1546 /* get a particular key */
1547 int
1548 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1549 {
1550     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1551     register int i, maxa;
1552     register struct afsconf_key *tk;
1553     register afs_int32 code;
1554
1555     LOCK_GLOBAL_MUTEX;
1556     code = afsconf_Check(adir);
1557     if (code) {
1558         UNLOCK_GLOBAL_MUTEX;
1559         return AFSCONF_FAILURE;
1560     }
1561     maxa = adir->keystr->nkeys;
1562
1563     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1564         if (tk->kvno == avno) {
1565             memcpy(akey, tk->key, 8);
1566             UNLOCK_GLOBAL_MUTEX;
1567             return 0;
1568         }
1569     }
1570
1571     UNLOCK_GLOBAL_MUTEX;
1572     return AFSCONF_NOTFOUND;
1573 }
1574
1575 /* save the key structure in the appropriate file */
1576 static int
1577 SaveKeys(struct afsconf_dir *adir)
1578 {
1579     struct afsconf_keys tkeys;
1580     register int fd;
1581     register afs_int32 i;
1582     char tbuffer[256];
1583
1584     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1585
1586     /* convert it to net byte order */
1587     for (i = 0; i < tkeys.nkeys; i++)
1588         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1589     tkeys.nkeys = htonl(tkeys.nkeys);
1590
1591     /* rewrite keys file */
1592     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1593     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1594     if (fd < 0)
1595         return AFSCONF_FAILURE;
1596     i = write(fd, &tkeys, sizeof(tkeys));
1597     if (i != sizeof(tkeys)) {
1598         close(fd);
1599         return AFSCONF_FAILURE;
1600     }
1601     if (close(fd) < 0)
1602         return AFSCONF_FAILURE;
1603     return 0;
1604 }
1605
1606 int
1607 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1608                afs_int32 overwrite)
1609 {
1610     register struct afsconf_keys *tk;
1611     register struct afsconf_key *tkey;
1612     register afs_int32 i;
1613     int foundSlot;
1614
1615     LOCK_GLOBAL_MUTEX;
1616     tk = adir->keystr;
1617
1618     if (akvno != 999) {
1619         if (akvno < 0 || akvno > 255) {
1620             UNLOCK_GLOBAL_MUTEX;
1621             return ERANGE;
1622         }
1623     }
1624     foundSlot = 0;
1625     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1626         if (tkey->kvno == akvno) {
1627             if (!overwrite) {
1628                 UNLOCK_GLOBAL_MUTEX;
1629                 return AFSCONF_KEYINUSE;
1630             }
1631             foundSlot = 1;
1632             break;
1633         }
1634     }
1635     if (!foundSlot) {
1636         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1637             UNLOCK_GLOBAL_MUTEX;
1638             return AFSCONF_FULL;
1639         }
1640         tkey = &tk->key[tk->nkeys++];
1641     }
1642     tkey->kvno = akvno;
1643     memcpy(tkey->key, akey, 8);
1644     i = SaveKeys(adir);
1645     afsconf_Touch(adir);
1646     UNLOCK_GLOBAL_MUTEX;
1647     return i;
1648 }
1649
1650 /* this proc works by sliding the other guys down, rather than using a funny
1651     kvno value, so that callers can count on getting a good key in key[0].
1652 */
1653 int
1654 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1655 {
1656     register struct afsconf_keys *tk;
1657     register struct afsconf_key *tkey;
1658     register int i;
1659     int foundFlag = 0;
1660
1661     LOCK_GLOBAL_MUTEX;
1662     tk = adir->keystr;
1663
1664     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1665         if (tkey->kvno == akvno) {
1666             foundFlag = 1;
1667             break;
1668         }
1669     }
1670     if (!foundFlag) {
1671         UNLOCK_GLOBAL_MUTEX;
1672         return AFSCONF_NOTFOUND;
1673     }
1674
1675     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1676     for (; i < tk->nkeys - 1; i++, tkey++) {
1677         tkey->kvno = (tkey + 1)->kvno;
1678         memcpy(tkey->key, (tkey + 1)->key, 8);
1679     }
1680     tk->nkeys--;
1681     i = SaveKeys(adir);
1682     afsconf_Touch(adir);
1683     UNLOCK_GLOBAL_MUTEX;
1684     return i;
1685 }