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