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