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