afs_FindService should handle iana portnames
[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     register afs_int32 i;
1129     int tservice;
1130     struct afsconf_entry DNSce;
1131     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1132     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1133     unsigned short ipRanks[AFSMAXCELLHOSTS];
1134     int numServers;
1135     int rc;
1136     int ttl;
1137
1138     DNSce.cellInfo.numServers = 0;
1139     DNSce.next = NULL;
1140     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, ipRanks, &numServers,
1141                       &ttl);
1142     /* ignore the ttl here since this code is only called by transitory programs
1143      * like klog, etc. */
1144     if (rc < 0)
1145         return -1;
1146     if (numServers == 0)
1147         return -1;
1148
1149     for (i = 0; i < numServers; i++) {
1150         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1151                sizeof(long));
1152         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1153         acellInfo->hostAddr[i].sin_family = AF_INET;
1154
1155         /* sin_port supplied by connection code */
1156     }
1157
1158     acellInfo->numServers = numServers;
1159     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1160     if (aservice) {
1161         LOCK_GLOBAL_MUTEX;
1162         tservice = afsconf_FindService(aservice);
1163         UNLOCK_GLOBAL_MUTEX;
1164         if (tservice < 0) {
1165             return AFSCONF_NOTFOUND;    /* service not found */
1166         }
1167         for (i = 0; i < acellInfo->numServers; i++) {
1168             acellInfo->hostAddr[i].sin_port = tservice;
1169         }
1170     }
1171     acellInfo->linkedCell = NULL;       /* no linked cell */
1172     acellInfo->flags = 0;
1173     return 0;
1174 }
1175 #endif /* windows */
1176 #endif /* AFS_AFSDB_ENV */
1177
1178 int
1179 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1180                     struct afsconf_cell *acellInfo)
1181 {
1182     register struct afsconf_entry *tce;
1183     struct afsconf_aliasentry *tcae;
1184     struct afsconf_entry *bestce;
1185     register afs_int32 i;
1186     int tservice;
1187     char *tcell;
1188     int cnLen;
1189     int ambig;
1190     char tbuffer[64];
1191
1192     LOCK_GLOBAL_MUTEX;
1193     if (adir)
1194         afsconf_Check(adir);
1195     if (acellName) {
1196         tcell = acellName;
1197         cnLen = (int)(strlen(tcell) + 1);
1198         lcstring(tcell, tcell, cnLen);
1199         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1200         /* call to afsconf_GetLocalCell: like klog  */
1201     } else {
1202         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1203         if (i) {
1204             UNLOCK_GLOBAL_MUTEX;
1205             return i;
1206         }
1207         tcell = tbuffer;
1208     }
1209     cnLen = strlen(tcell);
1210     bestce = (struct afsconf_entry *)0;
1211     ambig = 0;
1212     if (!adir) {
1213         UNLOCK_GLOBAL_MUTEX;
1214         return 0;
1215     }
1216
1217     /* Look through the list of aliases */
1218     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1219         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1220             tcell = tcae->aliasInfo.realName;
1221             break;
1222         }
1223     }
1224
1225     for (tce = adir->entries; tce; tce = tce->next) {
1226         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1227             /* found our cell */
1228             bestce = tce;
1229             ambig = 0;
1230             break;
1231         }
1232         if (strlen(tce->cellInfo.name) < cnLen)
1233             continue;           /* clearly wrong */
1234         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1235             if (bestce)
1236                 ambig = 1;      /* ambiguous unless we get exact match */
1237             bestce = tce;
1238         }
1239     }
1240     if (!ambig && bestce && bestce->cellInfo.numServers) {
1241         *acellInfo = bestce->cellInfo;  /* structure assignment */
1242         if (aservice) {
1243             tservice = afsconf_FindService(aservice);
1244             if (tservice < 0) {
1245                 UNLOCK_GLOBAL_MUTEX;
1246                 return AFSCONF_NOTFOUND;        /* service not found */
1247             }
1248             for (i = 0; i < acellInfo->numServers; i++) {
1249                 acellInfo->hostAddr[i].sin_port = tservice;
1250             }
1251         }
1252         acellInfo->timeout = 0;
1253
1254         /* 
1255          * Until we figure out how to separate out ubik server
1256          * queries from other server queries, only perform gethostbyname()
1257          * lookup on the specified hostnames for the client CellServDB files.
1258          */
1259         if (IsClientConfigDirectory(adir->name) && 
1260             !(acellInfo->flags & AFSCONF_CELL_FLAG_DNS_QUERIED)) {
1261             int j;
1262             short numServers=0;                                 /*Num active servers for the cell */
1263             struct sockaddr_in hostAddr[MAXHOSTSPERCELL];       /*IP addresses for cell's servers */
1264             char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS];       /*Names for cell's servers */
1265
1266             memset(&hostAddr, 0, sizeof(hostAddr));
1267             memset(&hostName, 0, sizeof(hostName));
1268
1269             for ( j=0; j<acellInfo->numServers && numServers < MAXHOSTSPERCELL; j++ ) {
1270                 struct hostent *he = gethostbyname(acellInfo->hostName[j]);
1271                 int foundAddr = 0;
1272
1273                 if (he && he->h_addrtype == AF_INET) {
1274                     int i;
1275                     /* obtain all the valid address from the list */
1276                     for (i=0 ; he->h_addr_list[i] && numServers < MAXHOSTSPERCELL; i++) {
1277                         /* check to see if this is a new address; if so insert it into the list */
1278                         int k, dup;
1279                         for (k=0, dup=0; !dup && k < numServers; k++) {
1280                             if (hostAddr[k].sin_addr.s_addr == *(u_long *)he->h_addr_list[i])
1281                                 dup = 1;
1282                         }
1283                         if (dup)
1284                             continue;
1285
1286                         hostAddr[numServers].sin_family = AF_INET;
1287                         hostAddr[numServers].sin_port = acellInfo->hostAddr[0].sin_port;
1288 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
1289                         hostAddr[numServers].sin_len = sizeof(struct sockaddr_in);
1290 #endif
1291                         memcpy(&hostAddr[numServers].sin_addr.s_addr, he->h_addr_list[i], sizeof(long));
1292                         strcpy(hostName[numServers], acellInfo->hostName[j]);
1293                         foundAddr = 1;
1294                         numServers++;
1295                     }
1296                 }
1297                 if (!foundAddr) {
1298                     hostAddr[numServers] = acellInfo->hostAddr[j];
1299                     strcpy(hostName[numServers], acellInfo->hostName[j]);
1300                     numServers++;
1301                 }
1302             }
1303
1304             for (i=0; i<numServers; i++) {
1305                 acellInfo->hostAddr[i] = hostAddr[i];
1306                 strcpy(acellInfo->hostName[i], hostName[i]);
1307             }
1308             acellInfo->numServers = numServers;
1309             acellInfo->flags |= AFSCONF_CELL_FLAG_DNS_QUERIED;
1310         }
1311         UNLOCK_GLOBAL_MUTEX;
1312         return 0;
1313     } else {
1314         UNLOCK_GLOBAL_MUTEX;
1315 #ifdef AFS_AFSDB_ENV
1316         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1317 #else
1318         return AFSCONF_NOTFOUND;
1319 #endif /* AFS_AFSDB_ENV */
1320     }
1321 }
1322
1323 int
1324 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1325                      afs_int32 alen)
1326 {
1327     static int afsconf_showcell = 0;
1328     char *afscell_path;
1329     afs_int32 code = 0;
1330
1331     LOCK_GLOBAL_MUTEX;
1332     /*
1333      * If a cell switch was specified in a command, then it should override the 
1334      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1335      * is set and the cell name in the adir structure is used.
1336      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1337      */
1338     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1339         if (!afsconf_showcell) {
1340             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1341                     afscell_path);
1342             afsconf_showcell = 1;
1343         }
1344         strncpy(aname, afscell_path, alen);
1345     } else {
1346         afsconf_Check(adir);
1347         if (adir->cellName) {
1348             strncpy(aname, adir->cellName, alen);
1349         } else
1350             code = AFSCONF_UNKNOWN;
1351     }
1352
1353     UNLOCK_GLOBAL_MUTEX;
1354     return (code);
1355 }
1356
1357 int
1358 afsconf_Close(struct afsconf_dir *adir)
1359 {
1360     LOCK_GLOBAL_MUTEX;
1361     afsconf_CloseInternal(adir);
1362     if (adir->name)
1363         free(adir->name);
1364     free(adir);
1365     UNLOCK_GLOBAL_MUTEX;
1366     return 0;
1367 }
1368
1369 static int
1370 afsconf_CloseInternal(register struct afsconf_dir *adir)
1371 {
1372     register struct afsconf_entry *td, *nd;
1373     struct afsconf_aliasentry *ta, *na;
1374     register char *tname;
1375
1376     tname = adir->name;         /* remember name, since that's all we preserve */
1377
1378     /* free everything we can find */
1379     if (adir->cellName)
1380         free(adir->cellName);
1381     for (td = adir->entries; td; td = nd) {
1382         nd = td->next;
1383         if (td->cellInfo.linkedCell)
1384             free(td->cellInfo.linkedCell);
1385         free(td);
1386     }
1387     for (ta = adir->alias_entries; ta; ta = na) {
1388         na = ta->next;
1389         free(ta);
1390     }
1391     if (adir->keystr)
1392         free(adir->keystr);
1393
1394     /* reinit */
1395     memset(adir, 0, sizeof(struct afsconf_dir));
1396     adir->name = tname;         /* restore it */
1397     return 0;
1398 }
1399
1400 static int
1401 afsconf_Reopen(register struct afsconf_dir *adir)
1402 {
1403     register afs_int32 code;
1404     code = afsconf_CloseInternal(adir);
1405     if (code)
1406         return code;
1407     code = afsconf_OpenInternal(adir, 0, 0);
1408     return code;
1409 }
1410
1411 /* called during opening of config file */
1412 int
1413 afsconf_IntGetKeys(struct afsconf_dir *adir)
1414 {
1415     char tbuffer[256];
1416     register int fd;
1417     struct afsconf_keys *tstr;
1418     register afs_int32 code;
1419
1420 #ifdef AFS_NT40_ENV
1421     /* NT client config dir has no KeyFile; don't risk attempting open
1422      * because there might be a random file of this name if dir is shared.
1423      */
1424     if (IsClientConfigDirectory(adir->name)) {
1425         adir->keystr = ((struct afsconf_keys *)
1426                         malloc(sizeof(struct afsconf_keys)));
1427         adir->keystr->nkeys = 0;
1428         return 0;
1429     }
1430 #endif /* AFS_NT40_ENV */
1431
1432     LOCK_GLOBAL_MUTEX;
1433     /* compute the key name and other setup */
1434     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1435     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1436     adir->keystr = tstr;
1437
1438     /* read key file */
1439     fd = open(tbuffer, O_RDONLY);
1440     if (fd < 0) {
1441         tstr->nkeys = 0;
1442         UNLOCK_GLOBAL_MUTEX;
1443         return 0;
1444     }
1445     code = read(fd, tstr, sizeof(struct afsconf_keys));
1446     close(fd);
1447     if (code < sizeof(afs_int32)) {
1448         tstr->nkeys = 0;
1449         UNLOCK_GLOBAL_MUTEX;
1450         return 0;
1451     }
1452
1453     /* convert key structure to host order */
1454     tstr->nkeys = ntohl(tstr->nkeys);
1455
1456     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1457         tstr->nkeys = 0;
1458         UNLOCK_GLOBAL_MUTEX;
1459         return 0;
1460     }
1461
1462     for (fd = 0; fd < tstr->nkeys; fd++)
1463         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1464
1465     UNLOCK_GLOBAL_MUTEX;
1466     return 0;
1467 }
1468
1469 /* get keys structure */
1470 int
1471 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1472 {
1473     register afs_int32 code;
1474
1475     LOCK_GLOBAL_MUTEX;
1476     code = afsconf_Check(adir);
1477     if (code) {
1478         UNLOCK_GLOBAL_MUTEX;
1479         return AFSCONF_FAILURE;
1480     }
1481     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1482     UNLOCK_GLOBAL_MUTEX;
1483     return 0;
1484 }
1485
1486 /* get latest key */
1487 afs_int32
1488 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1489                      struct ktc_encryptionKey *akey)
1490 {
1491     register int i;
1492     int maxa;
1493     register struct afsconf_key *tk;
1494     register afs_int32 best;
1495     struct afsconf_key *bestk;
1496     register afs_int32 code;
1497
1498     LOCK_GLOBAL_MUTEX;
1499     code = afsconf_Check(adir);
1500     if (code) {
1501         UNLOCK_GLOBAL_MUTEX;
1502         return AFSCONF_FAILURE;
1503     }
1504     maxa = adir->keystr->nkeys;
1505
1506     best = -1;                  /* highest kvno we've seen yet */
1507     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1508     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1509         if (tk->kvno == 999)
1510             continue;           /* skip bcrypt keys */
1511         if (tk->kvno > best) {
1512             best = tk->kvno;
1513             bestk = tk;
1514         }
1515     }
1516     if (bestk) {                /* found any  */
1517         if (akey)
1518             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1519         if (avno)
1520             *avno = bestk->kvno;        /* and kvno to caller */
1521         UNLOCK_GLOBAL_MUTEX;
1522         return 0;
1523     }
1524     UNLOCK_GLOBAL_MUTEX;
1525     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1526 }
1527
1528 /* get a particular key */
1529 int
1530 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1531 {
1532     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1533     register int i, maxa;
1534     register struct afsconf_key *tk;
1535     register afs_int32 code;
1536
1537     LOCK_GLOBAL_MUTEX;
1538     code = afsconf_Check(adir);
1539     if (code) {
1540         UNLOCK_GLOBAL_MUTEX;
1541         return AFSCONF_FAILURE;
1542     }
1543     maxa = adir->keystr->nkeys;
1544
1545     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1546         if (tk->kvno == avno) {
1547             memcpy(akey, tk->key, 8);
1548             UNLOCK_GLOBAL_MUTEX;
1549             return 0;
1550         }
1551     }
1552
1553     UNLOCK_GLOBAL_MUTEX;
1554     return AFSCONF_NOTFOUND;
1555 }
1556
1557 /* save the key structure in the appropriate file */
1558 static int
1559 SaveKeys(struct afsconf_dir *adir)
1560 {
1561     struct afsconf_keys tkeys;
1562     register int fd;
1563     register afs_int32 i;
1564     char tbuffer[256];
1565
1566     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1567
1568     /* convert it to net byte order */
1569     for (i = 0; i < tkeys.nkeys; i++)
1570         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1571     tkeys.nkeys = htonl(tkeys.nkeys);
1572
1573     /* rewrite keys file */
1574     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1575     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1576     if (fd < 0)
1577         return AFSCONF_FAILURE;
1578     i = write(fd, &tkeys, sizeof(tkeys));
1579     if (i != sizeof(tkeys)) {
1580         close(fd);
1581         return AFSCONF_FAILURE;
1582     }
1583     if (close(fd) < 0)
1584         return AFSCONF_FAILURE;
1585     return 0;
1586 }
1587
1588 int
1589 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1590                afs_int32 overwrite)
1591 {
1592     register struct afsconf_keys *tk;
1593     register struct afsconf_key *tkey;
1594     register afs_int32 i;
1595     int foundSlot;
1596
1597     LOCK_GLOBAL_MUTEX;
1598     tk = adir->keystr;
1599
1600     if (akvno != 999) {
1601         if (akvno < 0 || akvno > 255) {
1602             UNLOCK_GLOBAL_MUTEX;
1603             return ERANGE;
1604         }
1605     }
1606     foundSlot = 0;
1607     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1608         if (tkey->kvno == akvno) {
1609             if (!overwrite) {
1610                 UNLOCK_GLOBAL_MUTEX;
1611                 return AFSCONF_KEYINUSE;
1612             }
1613             foundSlot = 1;
1614             break;
1615         }
1616     }
1617     if (!foundSlot) {
1618         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1619             UNLOCK_GLOBAL_MUTEX;
1620             return AFSCONF_FULL;
1621         }
1622         tkey = &tk->key[tk->nkeys++];
1623     }
1624     tkey->kvno = akvno;
1625     memcpy(tkey->key, akey, 8);
1626     i = SaveKeys(adir);
1627     afsconf_Touch(adir);
1628     UNLOCK_GLOBAL_MUTEX;
1629     return i;
1630 }
1631
1632 /* this proc works by sliding the other guys down, rather than using a funny
1633     kvno value, so that callers can count on getting a good key in key[0].
1634 */
1635 int
1636 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1637 {
1638     register struct afsconf_keys *tk;
1639     register struct afsconf_key *tkey;
1640     register int i;
1641     int foundFlag = 0;
1642
1643     LOCK_GLOBAL_MUTEX;
1644     tk = adir->keystr;
1645
1646     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1647         if (tkey->kvno == akvno) {
1648             foundFlag = 1;
1649             break;
1650         }
1651     }
1652     if (!foundFlag) {
1653         UNLOCK_GLOBAL_MUTEX;
1654         return AFSCONF_NOTFOUND;
1655     }
1656
1657     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1658     for (; i < tk->nkeys - 1; i++, tkey++) {
1659         tkey->kvno = (tkey + 1)->kvno;
1660         memcpy(tkey->key, (tkey + 1)->key, 8);
1661     }
1662     tk->nkeys--;
1663     i = SaveKeys(adir);
1664     afsconf_Touch(adir);
1665     UNLOCK_GLOBAL_MUTEX;
1666     return i;
1667 }