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