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