87a4820ac90a71acfb2500cf5fd840451e4baf41
[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 <afs/param.h>
11 #include <afsconfig.h>
12 #include <afs/stds.h>
13 #include <afs/pthread_glock.h>
14 #ifdef UKERNEL
15 #include "../afs/sysincludes.h"
16 #include "../afs/afsincludes.h"
17 #else /* UKERNEL */
18 #include <sys/types.h>
19 #ifdef AFS_NT40_ENV
20 #include <winsock2.h>
21 #include <sys/utime.h>
22 #include <io.h>
23 #include <WINNT/afssw.h>
24 #else
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
28 #include <sys/file.h>
29 #include <sys/time.h>
30 #ifdef AFS_AFSDB_ENV
31 #include <arpa/nameser.h>
32 #include <resolv.h>
33 #endif /* AFS_AFSDB_ENV */
34 #endif /* AFS_NT40_ENV */
35 #include <errno.h>
36 #include <ctype.h>
37 #include <time.h>
38 #include <stdio.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #endif /* UKERNEL */
42 #include <afs/afsutil.h>
43 #include "cellconfig.h"
44 #include "keys.h"
45
46 static ParseHostLine();
47 static ParseCellLine();
48 static afsconf_OpenInternal();
49 static afsconf_CloseInternal();
50 static afsconf_Reopen();
51
52 static struct afsconf_servPair serviceTable [] = {
53     "afs",      7000,
54     "afscb",    7001,
55     "afsprot",  7002,
56     "afsvldb",  7003,
57     "afskauth", 7004,
58     "afsvol",   7005,
59     "afserror", 7006,
60     "afsnanny", 7007,
61     "afsupdate",7008,
62     "afsrmtsys",7009,
63     "afsres",   7010,   /* residency database for MR-AFS */
64     "afsremio", 7011,   /* remote I/O interface for MR-AFS */
65     0, 0                /* insert new services before this spot */
66 };
67
68 /*
69  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
70  * our code can tell if there is new info in the key files, the cell server db
71  * files or any of the other files (and reopen the thing) if the date on
72  * CellServDB changes.
73  */
74
75 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
76 static afs_int32 afsconf_FindService(aname)
77 register char *aname; {
78     /* lookup a service name */
79     struct servent *ts;
80     register struct afsconf_servPair *tsp;
81
82 #if     defined(AFS_OSF_ENV) || defined(AFS_DEC_ENV)
83     ts = getservbyname(aname, "");
84 #else
85     ts = getservbyname(aname, (char *) 0);
86 #endif
87     if (ts) {
88         /* we found it in /etc/services, so we use this value */
89         return ts->s_port;  /* already in network byte order */
90     }
91
92     /* not found in /etc/services, see if it is one of ours */
93     for(tsp = serviceTable;; tsp++) {
94         if (tsp->name == (char *) 0) return -1;
95         if (!strcmp(tsp->name, aname)) return htons(tsp->port);
96     }
97 }
98
99 static int TrimLine(abuffer)
100 char *abuffer; {
101     char tbuffer[256];
102     register char *tp;
103     register int tc;
104
105     tp = abuffer;
106     while (tc = *tp) {
107         if (!isspace(tc)) break;
108         tp++;
109     }
110     strcpy(tbuffer, tp);
111     strcpy(abuffer, tbuffer);
112     return 0;
113 }
114
115 #ifdef AFS_NT40_ENV
116 /*
117  * IsClientConfigDirectory() -- determine if path matches well-known
118  *     client configuration directory.
119  */
120 static int IsClientConfigDirectory(const char *path)
121 {
122     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
123     int i;
124
125     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
126         int cc = tolower(cdir[i]);
127         int pc = tolower(path[i]);
128
129         if (cc == '\\') {
130             cc = '/';
131         }
132         if (pc == '\\') {
133             pc = '/';
134         }
135         if (cc != pc) {
136             return 0;
137         }
138     }
139
140     /* hit end of one or both; allow mismatch in existence of trailing slash */
141     if (cdir[i] != '\0') {
142         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
143             return 0;
144         }
145     }
146     if (path[i] != '\0') {
147         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
148             return 0;
149         }
150     }
151     return 1;
152 }
153 #endif /* AFS_NT40_ENV */
154
155
156 static int afsconf_Check(adir)
157 register struct afsconf_dir *adir; {
158     char tbuffer[256];
159     struct stat tstat;
160     register afs_int32 code;
161
162 #ifdef AFS_NT40_ENV
163     /* NT client CellServDB has different file name than NT server or Unix */
164     if (IsClientConfigDirectory(adir->name)) {
165         strcompose(tbuffer, 256,
166                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
167     } else {
168         strcompose(tbuffer, 256,
169                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
170     }
171 #else
172     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
173 #endif /* AFS_NT40_ENV */
174
175     code = stat(tbuffer, &tstat);
176     if (code < 0) {
177         return code;
178     }
179     /* did file change? */
180     if (tstat.st_mtime == adir->timeRead) {
181         return 0;
182     }
183     /* otherwise file has changed, so reopen it */
184     return afsconf_Reopen(adir);
185 }
186
187 /* set modtime on file */
188 static afsconf_Touch(adir)
189 register struct afsconf_dir *adir; {
190     char tbuffer[256];
191     struct timeval tvp[2];
192
193 #ifdef AFS_NT40_ENV
194     /* NT client CellServDB has different file name than NT server or Unix */
195     if (IsClientConfigDirectory(adir->name)) {
196         strcompose(tbuffer, 256,
197                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
198     } else {
199         strcompose(tbuffer, 256,
200                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
201     }
202 #else
203     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
204 #endif /* AFS_NT40_ENV */
205
206     adir->timeRead = 0; /* just in case */
207 #ifdef AFS_NT40_ENV
208     return _utime(tbuffer, NULL);
209 #else
210     gettimeofday(&tvp[0], NULL);
211     tvp[1] = tvp[0];
212     return utimes(tbuffer, tvp);
213 #endif  /* AFS_NT40_ENV */
214 }
215
216 struct afsconf_dir *afsconf_Open(adir)
217 register char *adir; {
218     register struct afsconf_dir *tdir;
219     register afs_int32 code;
220
221     LOCK_GLOBAL_MUTEX
222     /* zero structure and fill in name; rest is done by internal routine */
223     tdir = (struct afsconf_dir *) malloc(sizeof(struct afsconf_dir));
224     bzero(tdir, sizeof(struct afsconf_dir));
225     tdir->name = (char *) malloc(strlen(adir)+1);
226     strcpy(tdir->name, adir);
227
228     code = afsconf_OpenInternal(tdir, 0, 0);
229     if (code) {
230         char *afsconf_path, *getenv(), afs_confdir[128];
231
232         free(tdir->name);
233         /* Check global place only when local Open failed for whatever reason */
234         if (!(afsconf_path = getenv("AFSCONF"))) {
235             /* 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... */
236             char *home_dir;
237             FILE *fp;
238             int len;
239
240             if (!(home_dir = getenv("HOME"))) {
241                 /* Our last chance is the "/.AFSCONF" file */
242                 fp = fopen("/.AFSCONF", "r");
243                 if (fp == 0) {
244                     free(tdir);
245                     UNLOCK_GLOBAL_MUTEX
246                     return (struct afsconf_dir *) 0;
247                 }
248                 fgets(afs_confdir, 128, fp);
249                 fclose(fp);
250             } else {
251                 char pathname[256];
252
253                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
254                 fp = fopen(pathname, "r");
255                 if (fp == 0) {
256                     /* Our last chance is the "/.AFSCONF" file */
257                     fp = fopen("/.AFSCONF", "r");
258                     if (fp == 0) {
259                         free(tdir);
260                         UNLOCK_GLOBAL_MUTEX
261                         return (struct afsconf_dir *) 0;
262                     }
263                     fgets(afs_confdir, 128, fp);
264                     fclose(fp);
265                 }
266                 fgets(afs_confdir, 128, fp);
267                 fclose(fp);             
268             }
269             len = strlen(afs_confdir);
270             if (len == 0) {
271                 free(tdir);
272                 UNLOCK_GLOBAL_MUTEX
273                 return (struct afsconf_dir *) 0;
274             }
275             if (afs_confdir[len-1] == '\n') {
276                 afs_confdir[len-1] = 0;
277             }
278             afsconf_path = afs_confdir;
279         }
280         tdir->name = (char *) malloc(strlen(afsconf_path)+1);
281         strcpy(tdir->name, afsconf_path);
282         code = afsconf_OpenInternal(tdir, 0, 0);
283         if (code) {
284             free(tdir->name);
285             free(tdir);
286             UNLOCK_GLOBAL_MUTEX
287             return (struct afsconf_dir *) 0;
288         }
289     }
290     UNLOCK_GLOBAL_MUTEX
291     return tdir;
292 }
293
294
295 static int GetCellUnix(struct afsconf_dir *adir)
296 {
297     int rc;
298     char tbuffer[256];
299     FILE *tf;
300
301     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
302     tf = fopen(tbuffer, "r");
303     if (tf) {
304         rc = fscanf(tf, "%s", tbuffer);
305         if (rc == 1) {
306             adir->cellName = (char *) malloc(strlen(tbuffer)+1);
307             strcpy(adir->cellName, tbuffer);
308         }
309         fclose(tf);
310     }
311     else {
312         return -1;
313     }
314     return 0;
315 }
316
317
318 #ifdef AFS_NT40_ENV
319 static int GetCellNT(struct afsconf_dir *adir)
320 {
321     if (IsClientConfigDirectory(adir->name)) {
322         /* NT client config dir; ThisCell is in registry (no file). */
323         return afssw_GetClientCellName(&adir->cellName);
324     } else {
325         /* NT server config dir; works just like Unix */
326         return GetCellUnix(adir);
327     }
328 }
329 #endif /* AFS_NT40_ENV */
330
331
332 static int afsconf_OpenInternal(adir, cell, clones)
333 register struct afsconf_dir *adir; 
334 char *cell;
335 char clones[];
336 {
337     FILE *tf;
338     register char *tp, *bp;
339     register struct afsconf_entry *curEntry;
340     register afs_int32 code;
341     afs_int32 i;
342     char tbuffer[256], tbuf1[256];
343     struct stat tstat;
344
345     /* figure out the cell name */
346 #ifdef AFS_NT40_ENV
347     i = GetCellNT(adir);
348 #else
349     i = GetCellUnix(adir);
350 #endif
351     if (i) {
352         return i;
353     }
354
355     /* now parse the individual lines */
356     curEntry = 0;
357
358 #ifdef AFS_NT40_ENV
359     /* NT client/server have a CellServDB that is the same format as Unix.
360      * However, the NT client uses a different file name
361      */
362     if (IsClientConfigDirectory(adir->name)) {
363         /* NT client config dir */
364         strcompose(tbuffer, 256,
365                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
366     } else {
367         /* NT server config dir */
368         strcompose(tbuffer, 256,
369                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
370     }
371 #else
372     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
373 #endif  /* AFS_NT40_ENV */
374
375     if (!stat(tbuffer, &tstat)) {
376         adir->timeRead = tstat.st_mtime;
377     } else {
378         adir->timeRead = 0;
379     }
380
381     strcpy(tbuf1, tbuffer);
382     tf = fopen(tbuffer, "r");
383     if (!tf) {
384         return -1;
385     }
386     while (1) {
387         tp = fgets(tbuffer, sizeof(tbuffer), tf);
388         if (!tp) break;
389         TrimLine(tbuffer);  /* remove white space */
390         if (tbuffer[0] == 0 || tbuffer[0] == '\n') continue;   /* empty line */
391         if (tbuffer[0] == '>') {
392             char linkedcell[MAXCELLCHARS];
393             /* start new cell item */
394             if (curEntry) {
395                 /* thread this guy on the list */
396                 curEntry->next = adir->entries;
397                 adir->entries = curEntry;
398                 curEntry = 0;
399             }
400             curEntry = (struct afsconf_entry *) malloc(sizeof(struct afsconf_entry));
401             bzero(curEntry, sizeof(struct afsconf_entry));
402             code = ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
403             if (code) {
404                 afsconf_CloseInternal(adir);
405                 fclose(tf);
406                 return -1;
407             }
408             if (linkedcell[0] != '\0') {
409                 curEntry->cellInfo.linkedCell =
410                     (char *) malloc(strlen(linkedcell) + 1);
411                 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
412             }
413         }
414         else {
415             /* new host in the current cell */
416             if (!curEntry) {
417                 afsconf_CloseInternal(adir);
418                 fclose(tf);
419                 return -1;
420             }
421             i = curEntry->cellInfo.numServers;
422            if (cell && !strcmp(cell, curEntry->cellInfo.name)) 
423                 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], &clones[i]);
424            else
425                 code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i], 0);
426             if (code) {
427                 if (code == AFSCONF_SYNTAX) {
428                     for (bp=tbuffer; *bp != '\n'; bp++) {       /* Take out the <cr> from the buffer */
429                         if (!*bp) break;
430                     }
431                     *bp= '\0';
432                     fprintf(stderr, "Can't properly parse host line \"%s\" in configuration file %s\n", tbuffer, tbuf1);
433                 }
434                 free(curEntry);
435                 fclose(tf);
436                 afsconf_CloseInternal(adir);
437                 return -1;
438             }
439             curEntry->cellInfo.numServers = ++i;
440         }
441     }
442     fclose(tf); /* close the file now */
443
444     /* end the last partially-completed cell */
445     if (curEntry) {
446         curEntry->next = adir->entries;
447         adir->entries = curEntry;
448     }
449     
450     /* now read the fs keys, if possible */
451     adir->keystr = (struct afsconf_keys *) 0;
452     afsconf_IntGetKeys(adir);
453
454     return 0;
455 }
456
457 /* parse a line of the form
458  *"128.2.1.3   #hostname" or
459  *"[128.2.1.3]  #hostname" for clones
460  * into the appropriate pieces.  
461  */
462 static ParseHostLine(aline, addr, aname, aclone)
463     char *aclone;
464     register struct sockaddr_in *addr;
465     char *aline, *aname; 
466 {
467     int c1, c2, c3, c4;
468     register afs_int32 code;
469     register char *tp;
470
471     if (*aline == '[') {
472         if (aclone) *aclone = 1;
473         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
474     } else {
475         if (aclone) *aclone = 0;
476         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
477     }
478     if (code != 5) return AFSCONF_SYNTAX;
479     addr->sin_family = AF_INET;
480     addr->sin_port = 0;
481     tp = (char *) &addr->sin_addr;
482     *tp++ = c1;
483     *tp++ = c2;
484     *tp++ = c3;
485     *tp++ = c4;
486     return 0;
487 }
488
489 /* parse a line of the form
490  * ">cellname [linkedcellname] [#comments]"
491  * into the appropriate pieces.
492  */
493 static ParseCellLine(aline, aname, alname)
494 register char *aline, *aname, *alname; {
495     register int code;
496     code = sscanf(aline, ">%s %s", aname, alname);
497     if (code == 1) *alname = '\0';
498     if (code == 2) {
499         if (*alname == '#') {
500             *alname = '\0';
501         }
502     }
503     return (code > 0 ? 0 : AFSCONF_SYNTAX);
504 }
505
506 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
507 afsconf_CellApply(adir, aproc, arock)
508 struct afsconf_dir *adir;
509 int (*aproc)();
510 char *arock; {
511     register struct afsconf_entry *tde;
512     register afs_int32 code;
513     LOCK_GLOBAL_MUTEX
514     for(tde=adir->entries; tde; tde=tde->next) {
515         code = (*aproc)(&tde->cellInfo, arock, adir);
516         if (code) {
517             UNLOCK_GLOBAL_MUTEX
518             return code;
519         }
520     }
521     UNLOCK_GLOBAL_MUTEX
522     return 0;
523 }
524
525 afs_int32 afsconf_SawCell = 0;
526
527 afsconf_GetExtendedCellInfo(adir, acellName, aservice, acellInfo, clones)
528     struct afsconf_dir *adir;
529     char *aservice;
530     char *acellName;
531     struct afsconf_cell *acellInfo; 
532     char clones[];
533 {
534     afs_int32 code;
535     char *cell;
536
537     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
538     if (code) 
539        return code;
540
541     if (acellName) 
542        cell = acellName;
543     else
544        cell = (char *) &acellInfo->name;
545
546     code = afsconf_OpenInternal(adir, cell, clones);
547     return code;
548 }
549
550 #ifdef AFS_AFSDB_ENV
551 afsconf_GetAfsdbInfo(acellName, aservice, acellInfo)
552     char *acellName;
553     char *aservice;
554     struct afsconf_cell *acellInfo;
555 {
556     afs_int32 code;
557     int tservice, len, i;
558     unsigned char answer[1024];
559     unsigned char *p;
560     char host[256];
561     int server_num = 0;
562     int minttl = 0;
563
564     /* The resolver isn't always MT-safe.. Perhaps this ought to be
565      * replaced with a more fine-grained lock just for the resolver
566      * operations.
567      */
568     LOCK_GLOBAL_MUTEX
569     len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
570     UNLOCK_GLOBAL_MUTEX
571
572     if (len < 0)
573         return AFSCONF_NOTFOUND;
574
575     p = answer + sizeof(HEADER);        /* Skip header */
576     code = dn_expand(answer, answer + len, p, host, sizeof(host));
577     if (code < 0)
578         return AFSCONF_NOTFOUND;
579     strncpy(acellInfo->name, host, sizeof(acellInfo->name));
580
581     p += code + QFIXEDSZ;       /* Skip name */
582
583     while (p < answer + len) {
584         int type, ttl, size;
585
586         code = dn_expand(answer, answer + len, p, host, sizeof(host));
587         if (code < 0)
588             return AFSCONF_NOTFOUND;
589
590         p += code;      /* Skip the name */
591         type = (p[0] << 8) | p[1];
592         p += 4;         /* Skip type and class */
593         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
594         p += 4;         /* Skip the TTL */
595         size = (p[0] << 8) | p[1];
596         p += 2;         /* Skip the size */
597
598         if (type == T_AFSDB) {
599             struct hostent *he;
600
601             code = dn_expand(answer, answer+len, p+2, host, sizeof(host));
602             if (code < 0)
603                 return AFSCONF_NOTFOUND;
604
605             /* Do we want to get TTL data for the A record as well? */
606             he = gethostbyname(host);
607             if (he && server_num < MAXHOSTSPERCELL) {
608                 afs_int32 ipaddr;
609                 memcpy(&ipaddr, he->h_addr, he->h_length);
610                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
611                 strncpy(acellInfo->hostName[server_num], host,
612                         sizeof(acellInfo->hostName[server_num]));
613                 server_num++;
614
615                 if (!minttl || ttl < minttl) minttl = ttl;
616             }
617         }
618
619         p += size;
620     }
621
622     if (server_num == 0)                /* No AFSDB records */
623         return AFSCONF_NOTFOUND;
624     acellInfo->numServers = server_num;
625
626     if (aservice) {
627         tservice = afsconf_FindService(aservice);
628         if (tservice < 0)
629             return AFSCONF_NOTFOUND;  /* service not found */
630         for (i=0; i<acellInfo->numServers; i++) {
631             acellInfo->hostAddr[i].sin_port = tservice;
632         }
633     }
634
635     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
636
637     return 0;
638 }
639 #endif /* AFS_AFSDB_ENV */
640
641 afsconf_GetCellInfo(adir, acellName, aservice, acellInfo)
642 struct afsconf_dir *adir;
643 char *aservice;
644 char *acellName;
645 struct afsconf_cell *acellInfo; {
646     register struct afsconf_entry *tce;
647     struct afsconf_entry *bestce;
648     register afs_int32 i;
649     int tservice;
650     char *tcell;
651     int cnLen, ambig;
652     char tbuffer[64];
653
654     LOCK_GLOBAL_MUTEX
655     if (adir) afsconf_Check(adir);
656     if (acellName) {
657         tcell = acellName;
658         cnLen = strlen(tcell)+1;
659         lcstring (tcell, tcell, cnLen);
660         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
661                                 /* call to afsconf_GetLocalCell: like klog  */
662     } else {
663         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
664         if (i) {
665             UNLOCK_GLOBAL_MUTEX
666             return i;
667         }
668         tcell = tbuffer;
669     }
670     cnLen = strlen(tcell);
671     bestce = (struct afsconf_entry *) 0;
672     ambig = 0;
673     if (!adir) {
674         UNLOCK_GLOBAL_MUTEX
675         return 0;
676     }
677     for(tce=adir->entries;tce;tce=tce->next) {
678         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
679             /* found our cell */
680             bestce = tce;
681             ambig = 0;
682             break;
683         }
684         if (strlen(tce->cellInfo.name) < cnLen) continue;   /* clearly wrong */
685         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
686             if (bestce) ambig = 1;  /* ambiguous unless we get exact match */
687             bestce = tce;
688         }
689     }
690     if (!ambig && bestce) {
691         *acellInfo = bestce->cellInfo;  /* structure assignment */
692         if (aservice) {
693             tservice = afsconf_FindService(aservice);
694             if (tservice < 0) {
695                 UNLOCK_GLOBAL_MUTEX
696                 return AFSCONF_NOTFOUND;  /* service not found */
697             }
698             for(i=0;i<acellInfo->numServers;i++) {
699                 acellInfo->hostAddr[i].sin_port = tservice;
700             }
701         }
702         acellInfo->timeout = 0;
703         UNLOCK_GLOBAL_MUTEX
704         return 0;
705     }
706     else {
707         UNLOCK_GLOBAL_MUTEX
708 #ifdef AFS_AFSDB_ENV
709         return afsconf_GetAfsdbInfo(acellName, aservice, acellInfo);
710 #else
711         return AFSCONF_NOTFOUND;
712 #endif /* AFS_AFSDB_ENV */
713     }
714 }
715
716 afsconf_GetLocalCell(adir, aname, alen)
717 register struct afsconf_dir *adir;
718 char *aname;
719 afs_int32 alen; {
720     static int  afsconf_showcell = 0;
721     char        *afscell_path;
722     char        *getenv();
723     afs_int32        code = 0;
724
725    LOCK_GLOBAL_MUTEX
726    /*
727     * If a cell switch was specified in a command, then it should override the 
728     * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
729     * is set and the cell name in the adir structure is used.
730     * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
731     */
732    if ( !afsconf_SawCell && (afscell_path= getenv("AFSCELL")) ) {     
733         if ( !afsconf_showcell ) {
734             fprintf(stderr, "Note: Operation is performed on cell %s\n", afscell_path);
735             afsconf_showcell = 1;
736         }
737         strncpy(aname, afscell_path, alen);
738     } else {                                    
739         afsconf_Check(adir);
740         if (adir->cellName) {
741             strncpy(aname, adir->cellName, alen);
742         }
743         else code = AFSCONF_UNKNOWN;
744     }
745
746     UNLOCK_GLOBAL_MUTEX
747     return(code);
748 }
749
750 afsconf_Close(adir)
751 struct afsconf_dir *adir; {
752     LOCK_GLOBAL_MUTEX
753     afsconf_CloseInternal(adir);
754     if (adir->name) free(adir->name);
755     free(adir);
756     UNLOCK_GLOBAL_MUTEX
757     return 0;
758 }
759
760 static int afsconf_CloseInternal(adir)
761 register struct afsconf_dir *adir; {
762     register struct afsconf_entry *td, *nd;
763     register char *tname;
764
765     tname = adir->name; /* remember name, since that's all we preserve */
766
767     /* free everything we can find */
768     if (adir->cellName) free(adir->cellName);
769     for(td=adir->entries;td;td=nd) {
770         nd = td->next;
771         if (td->cellInfo.linkedCell)
772             free(td->cellInfo.linkedCell);
773         free(td);
774     }
775     if (adir->keystr) free(adir->keystr);
776
777     /* reinit */
778     bzero(adir, sizeof(struct afsconf_dir));
779     adir->name = tname;     /* restore it */
780     return 0;
781 }
782
783 static int afsconf_Reopen(adir)
784 register struct afsconf_dir *adir; {
785     register afs_int32 code;
786     code = afsconf_CloseInternal(adir);
787     if (code) return code;
788     code = afsconf_OpenInternal(adir, 0, 0);
789     return code;
790 }
791
792 /* called during opening of config file */
793 afsconf_IntGetKeys(adir)
794 struct afsconf_dir *adir;
795 {
796     char tbuffer[256];
797     register int fd;
798     struct afsconf_keys *tstr;
799     register afs_int32 code;
800
801 #ifdef AFS_NT40_ENV
802     /* NT client config dir has no KeyFile; don't risk attempting open
803      * because there might be a random file of this name if dir is shared.
804      */
805     if (IsClientConfigDirectory(adir->name)) {
806         adir->keystr = ((struct afsconf_keys *)
807                         malloc(sizeof(struct afsconf_keys)));
808         adir->keystr->nkeys = 0;
809         return 0;
810     }
811 #endif /* AFS_NT40_ENV */
812
813     LOCK_GLOBAL_MUTEX
814     /* compute the key name and other setup */
815
816     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
817     tstr = (struct afsconf_keys *) malloc(sizeof (struct afsconf_keys));
818     adir->keystr = tstr;
819
820     /* read key file */
821     fd = open(tbuffer, O_RDONLY);
822     if (fd < 0) {
823         tstr->nkeys = 0;
824         UNLOCK_GLOBAL_MUTEX
825         return 0;
826     }
827     code = read(fd, tstr, sizeof(struct afsconf_keys));
828     close(fd);
829     if (code < sizeof(afs_int32)) {
830         tstr->nkeys = 0;
831         UNLOCK_GLOBAL_MUTEX
832         return 0;
833     }
834
835     /* convert key structure to host order */
836     tstr->nkeys = ntohl(tstr->nkeys);
837     for(fd=0;fd<tstr->nkeys;fd++)
838         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
839
840     UNLOCK_GLOBAL_MUTEX
841     return 0;
842 }
843
844 /* get keys structure */
845 afsconf_GetKeys(adir, astr)
846 struct afsconf_dir *adir;
847 struct afsconf_keys *astr;
848 {
849     register afs_int32 code;
850
851     LOCK_GLOBAL_MUTEX
852     code = afsconf_Check(adir);
853     if (code)
854         return AFSCONF_FAILURE;
855     bcopy(adir->keystr, astr, sizeof(struct afsconf_keys));
856     UNLOCK_GLOBAL_MUTEX
857     return 0;
858 }
859
860 /* get latest key */
861 afs_int32 afsconf_GetLatestKey(adir, avno, akey)
862   IN struct afsconf_dir *adir;
863   OUT afs_int32 *avno;
864   OUT char *akey;
865 {
866     register int i;
867     int maxa;
868     register struct afsconf_key *tk;
869     register afs_int32 best;
870     struct afsconf_key *bestk;
871     register afs_int32 code;
872     
873     LOCK_GLOBAL_MUTEX
874     code = afsconf_Check(adir);
875     if (code)
876         return AFSCONF_FAILURE;
877     maxa = adir->keystr->nkeys;
878
879     best = -1;      /* highest kvno we've seen yet */
880     bestk = (struct afsconf_key *) 0;   /* ptr to structure providing best */
881     for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
882         if (tk->kvno == 999) continue;  /* skip bcrypt keys */
883         if (tk->kvno > best) {
884             best = tk->kvno;
885             bestk = tk;
886         }
887     }
888     if (bestk) {    /* found any  */
889         if (akey) bcopy(bestk->key, akey, 8); /* copy out latest key */
890         if (avno) *avno = bestk->kvno;  /* and kvno to caller */
891         UNLOCK_GLOBAL_MUTEX
892         return 0;
893     }
894     UNLOCK_GLOBAL_MUTEX
895     return AFSCONF_NOTFOUND;    /* didn't find any keys */
896 }
897
898 /* get a particular key */
899 afsconf_GetKey(adir, avno, akey)
900 struct afsconf_dir *adir;
901 afs_int32 avno;
902 char *akey;
903 {
904     register int i, maxa;
905     register struct afsconf_key *tk;
906     register afs_int32 code;
907
908     LOCK_GLOBAL_MUTEX
909     code = afsconf_Check(adir);
910     if (code)
911         return AFSCONF_FAILURE;
912     maxa = adir->keystr->nkeys;
913
914     for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
915         if (tk->kvno == avno) {
916             bcopy(tk->key, akey, 8);
917             UNLOCK_GLOBAL_MUTEX
918             return 0;
919         }
920     }
921
922     UNLOCK_GLOBAL_MUTEX
923     return AFSCONF_NOTFOUND;
924 }
925
926 /* save the key structure in the appropriate file */
927 static SaveKeys(adir)
928 struct afsconf_dir *adir;
929 {
930     struct afsconf_keys tkeys;
931     register int fd;
932     register afs_int32 i;
933     char tbuffer[256];
934
935     bcopy(adir->keystr, &tkeys, sizeof(struct afsconf_keys));
936
937     /* convert it to net byte order */
938     for(i = 0; i<tkeys.nkeys; i++ )
939         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
940     tkeys.nkeys = htonl(tkeys.nkeys);
941     
942     /* rewrite keys file */
943     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
944     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
945     if (fd < 0) return AFSCONF_FAILURE;
946     i = write(fd, &tkeys, sizeof(tkeys));
947     if (i != sizeof(tkeys)) {
948         close(fd);
949         return AFSCONF_FAILURE;
950     }
951     if (close(fd) < 0) return AFSCONF_FAILURE;
952     return 0;
953 }
954
955 afsconf_AddKey(adir, akvno, akey, overwrite)
956 struct afsconf_dir *adir;
957 afs_int32 akvno, overwrite;
958 char akey[8];
959 {
960     register struct afsconf_keys *tk;
961     register struct afsconf_key *tkey;
962     register afs_int32 i;
963     int foundSlot;
964
965     LOCK_GLOBAL_MUTEX
966     tk = adir->keystr;
967     
968     if (akvno != 999) {
969         if (akvno < 0 || akvno > 255) {
970             UNLOCK_GLOBAL_MUTEX
971             return ERANGE;
972         }
973     }
974     foundSlot = 0;
975     for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
976         if (tkey->kvno == akvno) {
977             if (!overwrite) {
978                 UNLOCK_GLOBAL_MUTEX
979                 return AFSCONF_KEYINUSE;
980             }
981             foundSlot = 1;
982             break;
983         }
984     }
985     if (!foundSlot) {
986         if (tk->nkeys >= AFSCONF_MAXKEYS) {
987             UNLOCK_GLOBAL_MUTEX
988             return AFSCONF_FULL;
989         }
990         tkey = &tk->key[tk->nkeys++];
991     }
992     tkey->kvno = akvno;
993     bcopy(akey, tkey->key, 8);
994     i = SaveKeys(adir);
995     afsconf_Touch(adir);
996     UNLOCK_GLOBAL_MUTEX
997     return i;
998 }
999
1000 /* this proc works by sliding the other guys down, rather than using a funny
1001     kvno value, so that callers can count on getting a good key in key[0].
1002 */
1003 afsconf_DeleteKey(adir, akvno)
1004 struct afsconf_dir *adir;
1005 afs_int32 akvno;
1006 {
1007     register struct afsconf_keys *tk;
1008     register struct afsconf_key *tkey;
1009     register int i;
1010     int foundFlag = 0;
1011
1012     LOCK_GLOBAL_MUTEX
1013     tk = adir->keystr;
1014
1015     for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
1016         if (tkey->kvno == akvno) {
1017             foundFlag = 1;
1018             break;
1019         }
1020     }
1021     if (!foundFlag) {
1022         UNLOCK_GLOBAL_MUTEX
1023         return AFSCONF_NOTFOUND;
1024     }
1025
1026     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1027     for(;i<tk->nkeys-1; i++,tkey++) {
1028         tkey->kvno = (tkey+1)->kvno;
1029         bcopy((tkey+1)->key, tkey->key, 8);
1030     }
1031     tk->nkeys--;
1032     i = SaveKeys(adir);
1033     afsconf_Touch(adir);
1034     UNLOCK_GLOBAL_MUTEX
1035     return i;
1036 }