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