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