institute afsdb dns lookup timeout if we can
[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
14 #include <afs/stds.h>
15 #include <afs/pthread_glock.h>
16 #ifdef UKERNEL
17 #include "afs/sysincludes.h"
18 #include "afsincludes.h"
19 #include "des/des.h"
20 #include "rx/rxkad.h"
21 #include <netdb.h>
22 #include <ctype.h>
23 #else /* UKERNEL */
24 #include <sys/types.h>
25 #ifdef AFS_NT40_ENV
26 #include <winsock2.h>
27 #include <sys/utime.h>
28 #include <io.h>
29 #include <WINNT/afssw.h>
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 #include <ctype.h>
37 #include <arpa/nameser.h>
38 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
39 #include <arpa/nameser_compat.h>
40 #endif
41 #include <resolv.h>
42 #endif /* AFS_NT40_ENV */
43 #include <afs/afsint.h>
44 #include <errno.h>
45 #include <ctype.h>
46 #include <time.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <sys/stat.h>
50 #include <fcntl.h>
51 #include <string.h>
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #include <rx/rxkad.h>
56 #include <rx/rx.h>
57 #endif /* UKERNEL */
58 #include <afs/afsutil.h>
59 #include "cellconfig.h"
60 #include "keys.h"
61 #ifdef AFS_NT40_ENV
62 #include <cm.h>
63 #include <cm_config.h>
64 /* cm_dns.h depends on cellconfig.h */
65 #include <cm_nls.h>
66 #include <cm_dns.h>
67 #endif
68 #include <rx/rx.h>
69 #include <rx/rxkad.h>
70
71 struct afsconf_servPair {
72     const char *name;
73     const char *ianaName;
74     int port;
75 };
76
77 static struct afsconf_servPair serviceTable[] = {
78     {"afs", "afs3-fileserver", 7000,},
79     {"afscb", "afs3-callback", 7001,},
80     {"afsprot", "afs3-prserver", 7002,},
81     {"afsvldb", "afs3-vlserver", 7003,},
82     {"afskauth", "afs3-kaserver", 7004,},
83     {"afsvol", "afs3-volserver", 7005,},
84     {"afserror", "afs3-errors", 7006,},
85     {"afsnanny", "afs3-bos", 7007,},
86     {"afsupdate", "afs3-update", 7008,},
87     {"afsrmtsys", "afs3-rmtsys", 7009,},
88     {"afsres", NULL, 7010,},/* residency database for MR-AFS */
89     {"afsremio", NULL, 7011,},  /* remote I/O interface for MR-AFS */
90     {0, 0, 0}                   /* insert new services before this spot */
91 };
92
93 /* Prototypes */
94 static int TrimLine(char *abuffer, int abufsize);
95 static int IsClientConfigDirectory(const char *path);
96 #ifdef AFS_NT40_ENV
97 static int GetCellNT(struct afsconf_dir *adir);
98 #endif
99 static int afsconf_Check(register struct afsconf_dir *adir);
100 static int afsconf_Touch(register struct afsconf_dir *adir);
101 static int GetCellUnix(struct afsconf_dir *adir);
102 static int afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
103                                 char clones[]);
104 static int ParseHostLine(char *aline, register struct sockaddr_in *addr,
105                          char *aname, char *aclone);
106 static int ParseCellLine(register char *aline, register char *aname,
107                          register char *alname);
108 static int afsconf_CloseInternal(register struct afsconf_dir *adir);
109 static int afsconf_Reopen(register struct afsconf_dir *adir);
110 static int SaveKeys(struct afsconf_dir *adir);
111
112 #ifndef T_AFSDB
113 #define T_AFSDB 18              /* per RFC1183 section 1 */
114 #endif
115 #ifndef T_SRV
116 #define T_SRV 33                /* RFC2782 */
117 #endif
118
119 /*
120  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
121  * our code can tell if there is new info in the key files, the cell server db
122  * files or any of the other files (and reopen the thing) if the date on
123  * CellServDB changes.
124  */
125
126 #if defined(AFS_SUN5_ENV) && !defined(__sparcv9)
127 /* Solaris through 10 in 32 bit mode will return EMFILE if fopen can't
128    get an fd <= 255. We allow the fileserver to claim more fds than that.
129    This has always been a problem since pr_Initialize would have the same
130    issue, but hpr_Initialize makes it more likely that we would see this. 
131    Work around it. This is not generic. It's coded with the needs of
132    afsconf_* in mind only.
133
134    http://www.opensolaris.org/os/community/onnv/flag-days/pages/2006042001/
135 */
136
137 #define BUFFER 4096
138
139 struct afsconf_iobuffer {
140     int _file;
141     char *buffer;
142     char *ptr;
143     char *endptr;
144 };
145
146 typedef struct afsconf_iobuffer afsconf_FILE;
147
148 static afsconf_FILE *
149 afsconf_fopen(const char *fname, const char *fmode)
150 {
151     int fd;
152     afsconf_FILE *iop;
153     
154     if ((fd = open(fname, O_RDONLY)) == -1) {
155         return NULL;
156     }
157     
158     iop = malloc(sizeof(struct afsconf_iobuffer));
159     if (iop == NULL) {
160         (void) close(fd);
161         errno = ENOMEM;
162         return NULL;
163     }
164     iop->_file = fd;
165     iop->buffer = malloc(BUFFER);
166     if (iop->buffer == NULL) {
167         (void) close(fd);
168         free((void *) iop);
169         errno = ENOMEM;
170         return NULL;
171     }
172     iop->ptr = iop->buffer;
173     iop->endptr = iop->buffer;
174     return iop;
175 }
176
177 static int
178 afsconf_fclose(afsconf_FILE *iop)
179 {
180     if (iop == NULL) {
181         return 0;
182     }
183     close(iop->_file);
184     free((void *)iop->buffer);
185     free((void *)iop);
186     return 0;
187 }
188
189 static char *
190 afsconf_fgets(char *s, int n, afsconf_FILE *iop)
191 {
192     char *p;
193     
194     p = s;
195     for (;;) {
196         char c;
197         
198         if (iop->ptr == iop->endptr) {
199             ssize_t len;
200             
201             if ((len = read(iop->_file, (void *)iop->buffer, BUFFER)) == -1) {
202                 return NULL;
203             }
204             if (len == 0) {
205                 *p = 0;
206                 if (s == p) {
207                     return NULL;
208                 }
209                 return s;
210             }
211             iop->ptr = iop->buffer;
212             iop->endptr = iop->buffer + len;
213         }
214         c = *iop->ptr++;
215         *p++ = c;
216         if ((p - s) == (n - 1)) {
217             *p = 0;
218             return s;
219         }
220         if (c == '\n') {
221             *p = 0;
222             return s;
223         }
224     }
225 }
226 #define fopen afsconf_fopen
227 #define fclose afsconf_fclose
228 #define fgets afsconf_fgets
229 #else
230 #define afsconf_FILE FILE
231 #endif /* AFS_SUN5_ENV && ! __sparcv9 */
232
233 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
234 afs_int32
235 afsconf_FindService(const char *aname)
236 {
237     /* lookup a service name */
238     struct servent *ts;
239     struct afsconf_servPair *tsp;
240
241     if (aname == NULL || aname[0] == '\0')
242         return -1;
243
244 #if     defined(AFS_OSF_ENV) 
245     ts = getservbyname(aname, "");
246 #else
247     ts = (struct servent *) getservbyname(aname, NULL);
248 #endif
249     if (ts) {
250         /* we found it in /etc/services, so we use this value */
251         return ts->s_port;      /* already in network byte order */
252     }
253
254     /* not found in /etc/services, see if it is one of ours */
255     for (tsp = serviceTable; tsp->port; tsp++) {
256         if ((tsp->name && (!strcmp(tsp->name, aname)))
257             || (tsp->ianaName && (!strcmp(tsp->ianaName, aname))))
258             return htons(tsp->port);
259     }
260     return -1;
261 }
262
263 const char *
264 afsconf_FindIANAName(const char *aname)
265 {
266     /* lookup a service name */
267     struct afsconf_servPair *tsp;
268
269     if (aname == NULL || aname[0] == '\0')
270         return NULL;
271
272     /* see if it is one of ours */
273     for (tsp = serviceTable; tsp->port; tsp++) {
274         if ((tsp->name && (!strcmp(tsp->name, aname)))
275             || (tsp->ianaName && (!strcmp(tsp->ianaName, aname))))
276             return tsp->ianaName;
277     }
278     return NULL;
279 }
280
281 static int
282 TrimLine(char *abuffer, int abufsize)
283 {
284     char tbuffer[256];
285     register char *tp;
286     register int tc;
287
288     tp = abuffer;
289     while ((tc = *tp)) {
290         if (!isspace(tc))
291             break;
292         tp++;
293     }
294     strlcpy(tbuffer, tp, sizeof tbuffer);
295     strlcpy(abuffer, tbuffer, abufsize);
296     return 0;
297 }
298
299 /*
300  * IsClientConfigDirectory() -- determine if path matches well-known
301  *     client configuration directory.
302  */
303 #ifdef AFS_NT40_ENV
304 #define IS_SEP(x) ((x) == '\\' || (x) == '/')
305 #else /* AFS_NT40_ENV */
306 #define IS_SEP(x) ((x) == '/')
307 #endif /* AFS_NT40_ENV */
308 static int
309 IsClientConfigDirectory(const char *path)
310 {
311     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
312     int i, cc, pc;
313
314     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
315 #ifdef AFS_NT40_ENV
316         cc = tolower(cdir[i]);
317         pc = tolower(path[i]);
318
319         if (cc == '\\') {
320             cc = '/';
321         }
322         if (pc == '\\') {
323             pc = '/';
324         }
325 #else /* AFS_NT40_ENV */
326         cc = cdir[i];
327         pc = path[i];
328 #endif /* AFS_NT40_ENV */
329         if (cc != pc) {
330             return 0;
331         }
332     }
333
334     /* hit end of one or both; allow mismatch in existence of trailing slash */
335     if (cdir[i] != '\0') {
336         if (!IS_SEP(cdir[i]) || (cdir[i + 1] != '\0')) {
337             return 0;
338         }
339     }
340     if (path[i] != '\0') {
341         if (!IS_SEP(path[i]) || (path[i + 1] != '\0')) {
342             return 0;
343         }
344     }
345     return 1;
346 }
347
348
349 static int
350 afsconf_Check(register struct afsconf_dir *adir)
351 {
352     char tbuffer[256];
353 #ifdef AFS_NT40_ENV
354     char *p;
355 #endif
356     struct stat tstat;
357     register afs_int32 code;
358
359 #ifdef AFS_NT40_ENV
360     /* NT client CellServDB has different file name than NT server or Unix */
361     if (IsClientConfigDirectory(adir->name)) {
362         if (!afssw_GetClientCellServDBDir(&p)) {
363             strcompose(tbuffer, sizeof(tbuffer), p, "/",
364                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
365             free(p);
366         } else {
367             int len;
368             strncpy(tbuffer, adir->name, sizeof(tbuffer));
369             len = (int)strlen(tbuffer);
370             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
371                 strncat(tbuffer, "\\", sizeof(tbuffer));
372             }
373             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
374                     sizeof(tbuffer));
375             tbuffer[sizeof(tbuffer) - 1] = '\0';
376         }
377     } else {
378         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
379                    NULL);
380     }
381 #else
382     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
383 #endif /* AFS_NT40_ENV */
384
385     code = stat(tbuffer, &tstat);
386     if (code < 0) {
387         return code;
388     }
389     /* did file change? */
390     if (tstat.st_mtime == adir->timeRead) {
391         return 0;
392     }
393     /* otherwise file has changed, so reopen it */
394     return afsconf_Reopen(adir);
395 }
396
397 /* set modtime on file */
398 static int
399 afsconf_Touch(register struct afsconf_dir *adir)
400 {
401     char tbuffer[256];
402 #ifndef AFS_NT40_ENV
403     struct timeval tvp[2];
404 #else
405     char *p;
406 #endif
407
408     adir->timeRead = 0;         /* just in case */
409
410 #ifdef AFS_NT40_ENV
411     /* NT client CellServDB has different file name than NT server or Unix */
412
413     if (IsClientConfigDirectory(adir->name)) {
414         if (!afssw_GetClientCellServDBDir(&p)) {
415             strcompose(tbuffer, sizeof(tbuffer), p, "/",
416                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
417             free(p);
418         } else {
419             int len = (int)strlen(tbuffer);
420             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
421                 strncat(tbuffer, "\\", sizeof(tbuffer));
422             }
423             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
424                     sizeof(tbuffer));
425             tbuffer[sizeof(tbuffer) - 1] = '\0';
426         }
427     } else {
428         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
429                    NULL);
430     }
431
432     return _utime(tbuffer, NULL);
433
434 #else
435     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
436     gettimeofday(&tvp[0], NULL);
437     tvp[1] = tvp[0];
438     return utimes(tbuffer, tvp);
439 #endif /* AFS_NT40_ENV */
440 }
441
442 struct afsconf_dir *
443 afsconf_Open(register const char *adir)
444 {
445     register struct afsconf_dir *tdir;
446     register afs_int32 code;
447
448     LOCK_GLOBAL_MUTEX;
449     /* zero structure and fill in name; rest is done by internal routine */
450     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
451     memset(tdir, 0, sizeof(struct afsconf_dir));
452     tdir->name = strdup(adir);
453
454     code = afsconf_OpenInternal(tdir, 0, 0);
455     if (code) {
456         char *afsconf_path, afs_confdir[128];
457
458         free(tdir->name);
459         /* Check global place only when local Open failed for whatever reason */
460         if (!(afsconf_path = getenv("AFSCONF"))) {
461             /* 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... */
462             char *home_dir;
463             afsconf_FILE *fp;
464             size_t len;
465
466             if (!(home_dir = getenv("HOME"))) {
467                 /* Our last chance is the "/.AFSCONF" file */
468                 fp = fopen("/.AFSCONF", "r");
469                 if (fp == 0) {
470                     free(tdir);
471                     UNLOCK_GLOBAL_MUTEX;
472                     return (struct afsconf_dir *)0;
473                 }
474                 fgets(afs_confdir, 128, fp);
475                 fclose(fp);
476             } else {
477                 char pathname[256];
478
479                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
480                 fp = fopen(pathname, "r");
481                 if (fp == 0) {
482                     /* Our last chance is the "/.AFSCONF" file */
483                     fp = fopen("/.AFSCONF", "r");
484                     if (fp == 0) {
485                         free(tdir);
486                         UNLOCK_GLOBAL_MUTEX;
487                         return (struct afsconf_dir *)0;
488                     }
489                 }
490                 fgets(afs_confdir, 128, fp);
491                 fclose(fp);
492             }
493             len = strlen(afs_confdir);
494             if (len == 0) {
495                 free(tdir);
496                 UNLOCK_GLOBAL_MUTEX;
497                 return (struct afsconf_dir *)0;
498             }
499             if (afs_confdir[len - 1] == '\n') {
500                 afs_confdir[len - 1] = 0;
501             }
502             afsconf_path = afs_confdir;
503         }
504         tdir->name = strdup(afsconf_path);
505         code = afsconf_OpenInternal(tdir, 0, 0);
506         if (code) {
507             free(tdir->name);
508             free(tdir);
509             UNLOCK_GLOBAL_MUTEX;
510             return (struct afsconf_dir *)0;
511         }
512     }
513     UNLOCK_GLOBAL_MUTEX;
514     return tdir;
515 }
516
517 static int
518 GetCellUnix(struct afsconf_dir *adir)
519 {
520     char *rc;
521     char tbuffer[256];
522     char *start, *p;
523     afsconf_FILE *fp;
524     
525     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
526     fp = fopen(tbuffer, "r");
527     if (fp == 0) {
528         return -1;
529     }
530     rc = fgets(tbuffer, 256, fp);
531     fclose(fp);
532     if (rc == NULL)
533         return -1;
534
535     start = tbuffer;
536     while (*start != '\0' && isspace(*start))
537         start++;
538     p = start;
539     while (*p != '\0' && !isspace(*p))
540         p++;
541     *p = '\0';
542     if (*start == '\0')
543         return -1;
544
545     adir->cellName = strdup(start);
546     return 0;
547 }
548
549
550 #ifdef AFS_NT40_ENV
551 static int
552 GetCellNT(struct afsconf_dir *adir)
553 {
554     if (IsClientConfigDirectory(adir->name)) {
555         /* NT client config dir; ThisCell is in registry (no file). */
556         return afssw_GetClientCellName(&adir->cellName);
557     } else {
558         /* NT server config dir; works just like Unix */
559         return GetCellUnix(adir);
560     }
561 }
562
563 /* The following procedures and structs are used on Windows only
564  * to enumerate the Cell information distributed within the 
565  * Windows registry.  (See src/WINNT/afsd/cm_config.c)
566  */
567 typedef struct _cm_enumCellRegistry {
568     afs_uint32 client;  /* non-zero if client query */
569     struct afsconf_dir *adir;
570 } cm_enumCellRegistry_t;
571
572 static long
573 cm_serverConfigProc(void *rockp, struct sockaddr_in *addrp, 
574                     char *hostNamep, unsigned short rank)
575 {
576     struct afsconf_cell *cellInfop = (struct afsconf_cell *)rockp;
577
578     if (cellInfop->numServers == MAXHOSTSPERCELL)
579         return 0;
580
581     cellInfop->hostAddr[cellInfop->numServers] = *addrp;
582     strncpy(cellInfop->hostName[cellInfop->numServers], hostNamep, MAXHOSTCHARS);
583     cellInfop->hostName[cellInfop->numServers][MAXHOSTCHARS-1] = '\0';
584     cellInfop->numServers++;
585
586     return 0;
587 }
588
589 static long
590 cm_enumCellRegistryProc(void *rockp, char * cellNamep)
591 {
592     long code;
593     cm_enumCellRegistry_t *enump = (cm_enumCellRegistry_t *)rockp;
594     char linkedName[256] = "";
595     int timeout = 0;
596     struct afsconf_entry *newEntry;
597
598
599     newEntry = malloc(sizeof(struct afsconf_entry));
600     if (newEntry == NULL)
601         return ENOMEM;
602     newEntry->cellInfo.numServers = 0;
603
604     code = cm_SearchCellRegistry(enump->client, cellNamep, NULL, linkedName, cm_serverConfigProc, &newEntry->cellInfo);
605     if (code == CM_ERROR_FORCE_DNS_LOOKUP)
606         code = cm_SearchCellByDNS(cellNamep, NULL, &timeout, cm_serverConfigProc, &newEntry->cellInfo);
607
608     if (code == 0) {
609         strncpy(newEntry->cellInfo.name, cellNamep, MAXCELLCHARS);
610         newEntry->cellInfo.name[MAXCELLCHARS-1];
611         if (linkedName[0])
612             newEntry->cellInfo.linkedCell = strdup(linkedName);
613         else
614             newEntry->cellInfo.linkedCell = NULL;
615         newEntry->cellInfo.timeout = timeout;
616         newEntry->cellInfo.flags = 0;
617
618         newEntry->next = enump->adir->entries;
619         enump->adir->entries = newEntry;
620     } else {
621         free(newEntry);
622     }
623     return code;
624 }       
625 #endif /* AFS_NT40_ENV */
626
627
628 static int
629 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
630                      char clones[])
631 {
632     afsconf_FILE *tf;
633     register char *tp, *bp;
634     register struct afsconf_entry *curEntry;
635     struct afsconf_aliasentry *curAlias;
636     register afs_int32 code;
637     afs_int32 i;
638     char tbuffer[256], tbuf1[256];
639     struct stat tstat;
640 #ifdef AFS_NT40_ENV
641     cm_enumCellRegistry_t enumCellRegistry = {0, 0};
642 #endif /* AFS_NT40_ENV */
643
644     /* figure out the local cell name */
645 #ifdef AFS_NT40_ENV
646     i = GetCellNT(adir);
647     enumCellRegistry.adir = adir;
648 #else
649     i = GetCellUnix(adir);
650 #endif
651
652 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
653     if (i) {
654         return i;
655     }
656 #endif
657
658     /* now parse the individual lines */
659     curEntry = 0;
660
661 #ifdef AFS_NT40_ENV
662     /* NT client/server have a CellServDB that is the same format as Unix.
663      * However, the NT client uses a different file name
664      */
665     if (IsClientConfigDirectory(adir->name)) {
666         /* NT client config dir */
667         char *p;
668
669         enumCellRegistry.client = 1;
670
671         if (!afssw_GetClientCellServDBDir(&p)) {
672             strcompose(tbuffer, sizeof(tbuffer), p, "/",
673                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
674             free(p);
675         } else {
676             int len;
677             strncpy(tbuffer, adir->name, sizeof(tbuffer));
678             len = (int)strlen(tbuffer);
679             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
680                 strncat(tbuffer, "\\", sizeof(tbuffer));
681             }
682             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
683                     sizeof(tbuffer));
684             tbuffer[sizeof(tbuffer) - 1] = '\0';
685         }
686     } else {
687         /* NT server config dir */
688         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
689                    NULL);
690     }
691 #else
692     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
693 #endif /* AFS_NT40_ENV */
694
695     if (!stat(tbuffer, &tstat)) {
696         adir->timeRead = tstat.st_mtime;
697     } else {
698         adir->timeRead = 0;
699     }
700
701     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
702     tf = fopen(tbuffer, "r");
703     if (!tf) {
704         return -1;
705     }
706
707     /* The CellServDB file is now open.  
708      * The following code parses the contents of the 
709      * file and creates a list with the first cell entry
710      * in the CellServDB file at the end of the list.
711      * 
712      * No checking is performed for duplicates.
713      * The side effects of this process are that duplicate
714      * entries appended to the end of the CellServDB file
715      * take precedence and are found in a shorter period 
716      * of time.
717      */
718
719     while (1) {
720         tp = fgets(tbuffer, sizeof(tbuffer), tf);
721         if (!tp)
722             break;
723         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
724         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
725             continue;           /* empty line */
726         if (tbuffer[0] == '>') {
727             char linkedcell[MAXCELLCHARS];
728             /* start new cell item */
729             if (curEntry) {
730                 /* thread this guy on the list */
731                 curEntry->next = adir->entries;
732                 adir->entries = curEntry;
733                 curEntry = 0;
734             }
735             curEntry =
736                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
737             memset(curEntry, 0, sizeof(struct afsconf_entry));
738             code =
739                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
740             if (code) {
741                 afsconf_CloseInternal(adir);
742                 fclose(tf);
743                 free(curEntry);
744                 return -1;
745             }
746             if (linkedcell[0] != '\0')
747                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
748         } else {
749             /* new host in the current cell */
750             if (!curEntry) {
751                 afsconf_CloseInternal(adir);
752                 fclose(tf);
753                 return -1;
754             }
755             i = curEntry->cellInfo.numServers;
756             if (i < MAXHOSTSPERCELL) {
757                 if (cell && !strcmp(cell, curEntry->cellInfo.name))
758                     code =
759                         ParseHostLine(tbuffer, 
760                                       &curEntry->cellInfo.hostAddr[i],
761                                       curEntry->cellInfo.hostName[i], 
762                                       &clones[i]);
763                 else
764                     code =
765                         ParseHostLine(tbuffer, 
766                                       &curEntry->cellInfo.hostAddr[i],
767                                       curEntry->cellInfo.hostName[i], 0);
768
769                 if (code) {
770                     if (code == AFSCONF_SYNTAX) {
771                         for (bp = tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
772                             if (!*bp)
773                                 break;
774                         }
775                         *bp = '\0';
776                         fprintf(stderr,
777                                 "Can't properly parse host line \"%s\" in configuration file %s\n",
778                                 tbuffer, tbuf1);
779                     }
780                     free(curEntry);
781                     fclose(tf);
782                     afsconf_CloseInternal(adir);
783                     return -1;
784                 }
785                 curEntry->cellInfo.numServers = ++i;
786             } else {
787                 fprintf(stderr,
788                         "Too many hosts for cell %s in configuration file %s\n", 
789                         curEntry->cellInfo.name, tbuf1);
790             }
791         }
792     }
793     fclose(tf);                 /* close the file now */
794
795     /* end the last partially-completed cell */
796     if (curEntry) {
797         curEntry->next = adir->entries;
798         adir->entries = curEntry;
799     }
800
801 #ifdef AFS_NT40_ENV
802      /* 
803       * Windows maintains a CellServDB list in the Registry
804       * that supercedes the contents of the CellServDB file.
805       * Prepending these entries to the head of the list 
806       * is sufficient to enforce the precedence.
807       */
808      cm_EnumerateCellRegistry( enumCellRegistry.client,
809                                cm_enumCellRegistryProc,
810                                &enumCellRegistry);
811 #endif /* AFS_NT40_ENV */
812
813     /* Read in the alias list */
814     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
815
816     tf = fopen(tbuffer, "r");
817     while (tf) {
818         char *aliasPtr;
819
820         tp = fgets(tbuffer, sizeof(tbuffer), tf);
821         if (!tp)
822             break;
823         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
824
825         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
826             continue;           /* empty line */
827
828         tp = tbuffer;
829         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
830             tp++;
831         if (tp[0] == '\0')
832             continue;           /* invalid line */
833
834         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
835             0[tp++] = '\0';
836         if (tp[0] == '\0')
837             continue;           /* invalid line */
838
839         aliasPtr = tp;
840         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
841                && tp[0] != '\n')
842             tp++;
843         tp[0] = '\0';
844
845         curAlias = malloc(sizeof(*curAlias));
846         memset(curAlias, 0, sizeof(*curAlias));
847
848         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
849         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
850
851         curAlias->next = adir->alias_entries;
852         adir->alias_entries = curAlias;
853     }
854
855     if (tf != NULL)
856         fclose(tf);
857     /* now read the fs keys, if possible */
858     adir->keystr = (struct afsconf_keys *)0;
859     afsconf_IntGetKeys(adir);
860
861     return 0;
862 }
863
864 /* parse a line of the form
865  *"128.2.1.3   #hostname" or
866  *"[128.2.1.3]  #hostname" for clones
867  * into the appropriate pieces.  
868  */
869 static int
870 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
871               char *aclone)
872 {
873     int c1, c2, c3, c4;
874     register afs_int32 code;
875     register char *tp;
876
877     if (*aline == '[') {
878         if (aclone)
879             *aclone = 1;
880         /* FIXME: length of aname unknown here */
881         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
882     } else {
883         if (aclone)
884             *aclone = 0;
885         /* FIXME: length of aname unknown here */
886         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
887     }
888     if (code != 5)
889         return AFSCONF_SYNTAX;
890     addr->sin_family = AF_INET;
891     addr->sin_port = 0;
892 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
893     addr->sin_len = sizeof(struct sockaddr_in);
894 #endif
895     tp = (char *)&addr->sin_addr;
896     *tp++ = c1;
897     *tp++ = c2;
898     *tp++ = c3;
899     *tp++ = c4;
900     return 0;
901 }
902
903 /* parse a line of the form
904  * ">cellname [linkedcellname] [#comments]"
905  * into the appropriate pieces.
906  */
907 static int
908 ParseCellLine(register char *aline, register char *aname,
909               register char *alname)
910 {
911     register int code;
912     /* FIXME: length of aname, alname unknown here */
913     code = sscanf(aline, ">%s %s", aname, alname);
914     if (code == 1)
915         *alname = '\0';
916     if (code == 2) {
917         if (*alname == '#') {
918             *alname = '\0';
919         }
920     }
921     return (code > 0 ? 0 : AFSCONF_SYNTAX);
922 }
923
924 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
925 int
926 afsconf_CellApply(struct afsconf_dir *adir,
927                   int (*aproc) (struct afsconf_cell * cell, void *arock,
928                                 struct afsconf_dir * dir), void *arock)
929 {
930     register struct afsconf_entry *tde;
931     register afs_int32 code;
932     LOCK_GLOBAL_MUTEX;
933     for (tde = adir->entries; tde; tde = tde->next) {
934         code = (*aproc) (&tde->cellInfo, arock, adir);
935         if (code) {
936             UNLOCK_GLOBAL_MUTEX;
937             return code;
938         }
939     }
940     UNLOCK_GLOBAL_MUTEX;
941     return 0;
942 }
943
944 /* call aproc(entry, arock, adir) for all cell aliases.
945  * Proc must return 0, or we'll stop early and return the code it returns
946  */
947 int
948 afsconf_CellAliasApply(struct afsconf_dir *adir,
949                        int (*aproc) (struct afsconf_cellalias * alias,
950                                      void *arock, struct afsconf_dir * dir),
951                        void *arock)
952 {
953     register struct afsconf_aliasentry *tde;
954     register afs_int32 code;
955     LOCK_GLOBAL_MUTEX;
956     for (tde = adir->alias_entries; tde; tde = tde->next) {
957         code = (*aproc) (&tde->aliasInfo, arock, adir);
958         if (code) {
959             UNLOCK_GLOBAL_MUTEX;
960             return code;
961         }
962     }
963     UNLOCK_GLOBAL_MUTEX;
964     return 0;
965 }
966
967 afs_int32 afsconf_SawCell = 0;
968
969 int
970 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
971                             char *aservice, struct afsconf_cell *acellInfo,
972                             char clones[])
973 {
974     afs_int32 code;
975     char *cell;
976
977     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
978     if (code)
979         return code;
980
981     if (acellName)
982         cell = acellName;
983     else
984         cell = (char *)&acellInfo->name;
985
986     code = afsconf_OpenInternal(adir, cell, clones);
987     return code;
988 }
989
990 #if !defined(AFS_NT40_ENV)
991 int
992 afsconf_LookupServer(const char *service, const char *protocol,
993                      const char *cellName, unsigned short afsdbPort,
994                      int *cellHostAddrs, char cellHostNames[][MAXHOSTCHARS],
995                      unsigned short ports[], unsigned short ipRanks[],
996                      int *numServers, int *ttl, char **arealCellName)
997 {
998     int code = 0;
999     int len;
1000     unsigned char answer[1024];
1001     unsigned char *p;
1002     char *dotcellname;
1003     char *realCellName;
1004     int cellnamelength, fullnamelength;
1005     char host[256];
1006     int server_num = 0;
1007     int minttl = 0;
1008     int try_init = 0;
1009     int dnstype = 0;
1010     int pass = 0;
1011     char *IANAname = (char *) afsconf_FindIANAName(service);
1012     int tservice = afsconf_FindService(service);
1013
1014     realCellName = NULL;
1015
1016     *numServers = 0;
1017     *ttl = 0;
1018     if (tservice <= 0 || !IANAname)
1019         return AFSCONF_NOTFOUND;        /* service not found */
1020
1021     if (strchr(cellName,'.'))
1022         pass += 2;
1023
1024     cellnamelength=strlen(cellName); /* _ ._ . . \0 */
1025     fullnamelength=cellnamelength+strlen(protocol)+strlen(IANAname)+6;
1026     dotcellname=malloc(fullnamelength);
1027     if (!dotcellname)
1028         return AFSCONF_NOTFOUND;        /* service not found */
1029
1030 #ifdef HAVE_RES_RETRANSRETRY
1031     if ((_res.options & RES_INIT) == 0 && res_init() == -1)
1032       return (0);
1033
1034     /*
1035      * Rx timeout is typically 56 seconds; limit user experience to
1036      * similar timeout
1037      */
1038     _res.retrans = 18;
1039     _res.retry = 3;
1040 #endif
1041
1042  retryafsdb:
1043     switch (pass) {
1044     case 0:
1045         dnstype = T_SRV;
1046         code = snprintf(dotcellname, fullnamelength, "_%s._%s.%s.",
1047                  IANAname, protocol, cellName);
1048         break;
1049     case 1:
1050         dnstype = T_AFSDB;
1051         code = snprintf(dotcellname, fullnamelength, "%s.",
1052                  cellName);
1053         break;
1054     case 2:
1055         dnstype = T_SRV;
1056         code = snprintf(dotcellname, fullnamelength, "_%s._%s.%s",
1057                  IANAname, protocol, cellName);
1058         break;
1059     case 3:
1060         dnstype = T_AFSDB;
1061         code = snprintf(dotcellname, fullnamelength, "%s",
1062                  cellName);
1063         break;
1064     }
1065     if ((code < 0) || (code >= fullnamelength))
1066         goto findservererror;
1067     LOCK_GLOBAL_MUTEX;
1068     len = res_search(dotcellname, C_IN, dnstype, answer, sizeof(answer));
1069     UNLOCK_GLOBAL_MUTEX;
1070
1071     if (len < 0) {
1072         if (try_init < 1) {
1073             try_init++;
1074             res_init();
1075             goto retryafsdb;
1076         }
1077         if (pass < 3) {
1078             pass++;
1079             goto retryafsdb;
1080         } else {
1081             code = AFSCONF_NOTFOUND;
1082             goto findservererror;
1083         }
1084     }
1085
1086     p = answer + sizeof(HEADER);        /* Skip header */
1087     code = dn_expand(answer, answer + len, p, host, sizeof(host));
1088     if (code < 0) {
1089         code = AFSCONF_NOTFOUND;
1090         goto findservererror;
1091     }
1092
1093     p += code + QFIXEDSZ;       /* Skip name */
1094
1095     while (p < answer + len) {
1096         int type, ttl, size;
1097
1098         code = dn_expand(answer, answer + len, p, host, sizeof(host));
1099         if (code < 0) {
1100             code = AFSCONF_NOTFOUND;
1101             goto findservererror;
1102         }
1103
1104         p += code;              /* Skip the name */
1105         type = (p[0] << 8) | p[1];
1106         p += 4;                 /* Skip type and class */
1107         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
1108         p += 4;                 /* Skip the TTL */
1109         size = (p[0] << 8) | p[1];
1110         p += 2;                 /* Skip the size */
1111
1112         if (type == T_AFSDB) {
1113             struct hostent *he;
1114             short afsdb_type;
1115
1116             afsdb_type = (p[0] << 8) | p[1];
1117             if (afsdb_type == 1) {
1118                 /*
1119                  * We know this is an AFSDB record for our cell, of the
1120                  * right AFSDB type.  Write down the true cell name that
1121                  * the resolver gave us above.
1122                  */
1123                 realCellName = strdup(host);
1124             }
1125
1126             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
1127             if (code < 0) {
1128                 code = AFSCONF_NOTFOUND;
1129                 goto findservererror;
1130             }
1131
1132             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
1133                 /* Do we want to get TTL data for the A record as well? */
1134                 (he = gethostbyname(host))) {
1135                 afs_int32 ipaddr;
1136                 memcpy(&ipaddr, he->h_addr, he->h_length);
1137                 cellHostAddrs[server_num] = ipaddr;
1138                 ports[server_num] = afsdbPort;
1139                 ipRanks[server_num] = 0;
1140                 strncpy(cellHostNames[server_num], host,
1141                         sizeof(cellHostNames[server_num]));
1142                 server_num++;
1143
1144                 if (!minttl || ttl < minttl)
1145                     minttl = ttl;
1146             }
1147         }
1148         if (type == T_SRV) {
1149             struct hostent *he;
1150
1151             code = dn_expand(answer, answer + len, p + 6, host, sizeof(host));
1152             if (code < 0) {
1153                 code = AFSCONF_NOTFOUND;
1154                 goto findservererror;
1155             }
1156
1157             if ((server_num < MAXHOSTSPERCELL) &&
1158                 /* Do we want to get TTL data for the A record as well? */
1159                 (he = gethostbyname(host))) {
1160                 afs_int32 ipaddr;
1161                 memcpy(&ipaddr, he->h_addr, he->h_length);
1162                 cellHostAddrs[server_num] = ipaddr;
1163                 ipRanks[server_num] = (p[0] << 8) | p[1];
1164                 ports[server_num] = (p[4] << 8) | p[5];
1165                 /* weight = (p[2] << 8) | p[3]; */
1166                 strncpy(cellHostNames[server_num], host,
1167                         sizeof(cellHostNames[server_num]));
1168                 server_num++;
1169
1170                 if (!minttl || ttl < minttl)
1171                     minttl = ttl;
1172             }
1173         }
1174
1175         p += size;
1176     }
1177
1178     if (server_num == 0) {      /* No AFSDB or SRV records */
1179         code = AFSCONF_NOTFOUND;
1180         goto findservererror;
1181     }
1182
1183     if (realCellName) {
1184         /* Convert the real cell name to lowercase */
1185         for (p = (unsigned char *)realCellName; *p; p++)
1186             *p = tolower(*p);
1187     }
1188
1189     *arealCellName = realCellName;
1190
1191     *numServers = server_num;
1192     *ttl = minttl ? (time(0) + minttl) : 0;
1193
1194     if ( *numServers > 0 )
1195         code =  0;
1196     else
1197         code = AFSCONF_NOTFOUND;
1198
1199 findservererror:
1200     free(dotcellname);
1201     return code;
1202 }
1203
1204 int
1205 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1206                      struct afsconf_cell *acellInfo)
1207 {
1208     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1209     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1210     unsigned short ipRanks[AFSMAXCELLHOSTS];
1211     unsigned short ports[AFSMAXCELLHOSTS];
1212     char *realCellName = NULL;
1213     int ttl, numServers, i;
1214     char *service = aservice;
1215     int code;
1216     unsigned short afsdbport;
1217     if (!service) {
1218         service = "afs3-vlserver";
1219         afsdbport = htons(7003);
1220     } else {
1221         service = aservice;
1222         afsdbport = afsconf_FindService(service);
1223     }
1224     code = afsconf_LookupServer((const char *)service, "udp",
1225                                 (const char *)acellName, afsdbport,
1226                                 cellHostAddrs, cellHostNames,
1227                                 ports, ipRanks, &numServers, &ttl,
1228                                 &realCellName);
1229
1230     if (code == 0) {
1231         acellInfo->timeout = ttl;
1232         acellInfo->numServers = numServers;
1233         for (i = 0; i < numServers; i++) {
1234             memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1235                    sizeof(afs_int32));
1236             memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1237             acellInfo->hostAddr[i].sin_family = AF_INET;
1238             acellInfo->hostAddr[i].sin_port = ports[i];
1239
1240             if (realCellName)
1241                 strlcpy(acellInfo->name, realCellName,
1242                         sizeof(acellInfo->name));
1243         }
1244         acellInfo->linkedCell = NULL;       /* no linked cell */
1245         acellInfo->flags = 0;
1246     }
1247     return code;
1248 }
1249 #else /* windows */
1250 int
1251 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1252                      struct afsconf_cell *acellInfo)
1253 {
1254     afs_int32 i;
1255     int tservice = afsconf_FindService(aservice);   /* network byte order */
1256     const char *ianaName = afsconf_FindIANAName(aservice);
1257     struct afsconf_entry DNSce;
1258     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1259     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1260     unsigned short ipRanks[AFSMAXCELLHOSTS];
1261     unsigned short ports[AFSMAXCELLHOSTS];          /* network byte order */
1262     int numServers;
1263     int rc;
1264     int ttl;
1265
1266     if (tservice < 0) {
1267         if (aservice)
1268             return AFSCONF_NOTFOUND;
1269         else
1270             tservice = 0;       /* port will be assigned by caller */
1271     }
1272
1273     if (ianaName == NULL)
1274         ianaName = "afs3-vlserver";
1275
1276     DNSce.cellInfo.numServers = 0;
1277     DNSce.next = NULL;
1278
1279     rc = getAFSServer(ianaName, "udp", acellName, tservice,
1280                       cellHostAddrs, cellHostNames, ports, ipRanks, &numServers,
1281                       &ttl);
1282     /* ignore the ttl here since this code is only called by transitory programs
1283      * like klog, etc. */
1284
1285     /* If we couldn't find an entry for the requested service
1286      * and that service happens to be the prservice or kaservice
1287      * then fallback to searching for afs3-vlserver and assigning
1288      * the port number here. */
1289     if (rc < 0 && tservice == htons(7002) || tservice == htons(7004)) {
1290         rc = getAFSServer("afs3-vlserver", "udp", acellName, tservice,
1291                            cellHostAddrs, cellHostNames, ports, ipRanks, &numServers,
1292                            &ttl);
1293         if (rc >= 0) {
1294             for (i = 0; i < numServers; i++)
1295                 ports[i] = tservice;
1296         }
1297     }
1298
1299     if (rc < 0 || numServers == 0)
1300         return -1;
1301
1302     for (i = 0; i < numServers; i++) {
1303         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1304                sizeof(long));
1305         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1306         acellInfo->hostAddr[i].sin_family = AF_INET;
1307         if (aservice)
1308             acellInfo->hostAddr[i].sin_port = ports[i];
1309         else
1310             acellInfo->hostAddr[i].sin_port = 0;
1311     }
1312
1313     acellInfo->numServers = numServers;
1314     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1315     acellInfo->linkedCell = NULL;       /* no linked cell */
1316     acellInfo->flags = 0;
1317     return 0;
1318 }
1319 #endif /* windows */
1320
1321 int
1322 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1323                     struct afsconf_cell *acellInfo)
1324 {
1325     register struct afsconf_entry *tce;
1326     struct afsconf_aliasentry *tcae;
1327     struct afsconf_entry *bestce;
1328     register afs_int32 i;
1329     int tservice;
1330     char *tcell;
1331     int cnLen;
1332     int ambig;
1333     char tbuffer[64];
1334
1335     LOCK_GLOBAL_MUTEX;
1336     if (adir)
1337         afsconf_Check(adir);
1338     if (acellName) {
1339         tcell = acellName;
1340         cnLen = (int)(strlen(tcell) + 1);
1341         lcstring(tcell, tcell, cnLen);
1342         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1343         /* call to afsconf_GetLocalCell: like klog  */
1344     } else {
1345         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1346         if (i) {
1347             UNLOCK_GLOBAL_MUTEX;
1348             return i;
1349         }
1350         tcell = tbuffer;
1351     }
1352     cnLen = strlen(tcell);
1353     bestce = (struct afsconf_entry *)0;
1354     ambig = 0;
1355     if (!adir) {
1356         UNLOCK_GLOBAL_MUTEX;
1357         return 0;
1358     }
1359
1360     /* Look through the list of aliases */
1361     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1362         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1363             tcell = tcae->aliasInfo.realName;
1364             break;
1365         }
1366     }
1367
1368     for (tce = adir->entries; tce; tce = tce->next) {
1369         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1370             /* found our cell */
1371             bestce = tce;
1372             ambig = 0;
1373             break;
1374         }
1375         if (strlen(tce->cellInfo.name) < cnLen)
1376             continue;           /* clearly wrong */
1377         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1378             if (bestce)
1379                 ambig = 1;      /* ambiguous unless we get exact match */
1380             bestce = tce;
1381         }
1382     }
1383     if (!ambig && bestce && bestce->cellInfo.numServers) {
1384         *acellInfo = bestce->cellInfo;  /* structure assignment */
1385         if (aservice) {
1386             tservice = afsconf_FindService(aservice);
1387             if (tservice < 0) {
1388                 UNLOCK_GLOBAL_MUTEX;
1389                 return AFSCONF_NOTFOUND;        /* service not found */
1390             }
1391             for (i = 0; i < acellInfo->numServers; i++) {
1392                 acellInfo->hostAddr[i].sin_port = tservice;
1393             }
1394         }
1395         acellInfo->timeout = 0;
1396
1397         /* 
1398          * Until we figure out how to separate out ubik server
1399          * queries from other server queries, only perform gethostbyname()
1400          * lookup on the specified hostnames for the client CellServDB files.
1401          */
1402         if (IsClientConfigDirectory(adir->name) && 
1403             !(acellInfo->flags & AFSCONF_CELL_FLAG_DNS_QUERIED)) {
1404             int j;
1405             short numServers=0;                                 /*Num active servers for the cell */
1406             struct sockaddr_in hostAddr[MAXHOSTSPERCELL];       /*IP addresses for cell's servers */
1407             char hostName[MAXHOSTSPERCELL][MAXHOSTCHARS];       /*Names for cell's servers */
1408
1409             memset(&hostAddr, 0, sizeof(hostAddr));
1410             memset(&hostName, 0, sizeof(hostName));
1411
1412             for ( j=0; j<acellInfo->numServers && numServers < MAXHOSTSPERCELL; j++ ) {
1413                 struct hostent *he = gethostbyname(acellInfo->hostName[j]);
1414                 int foundAddr = 0;
1415
1416                 if (he && he->h_addrtype == AF_INET) {
1417                     int i;
1418                     /* obtain all the valid address from the list */
1419                     for (i=0 ; he->h_addr_list[i] && numServers < MAXHOSTSPERCELL; i++) {
1420                         /* check to see if this is a new address; if so insert it into the list */
1421                         int k, dup;
1422                         for (k=0, dup=0; !dup && k < numServers; k++) {
1423                             if (hostAddr[k].sin_addr.s_addr == *(u_long *)he->h_addr_list[i])
1424                                 dup = 1;
1425                         }
1426                         if (dup)
1427                             continue;
1428
1429                         hostAddr[numServers].sin_family = AF_INET;
1430                         hostAddr[numServers].sin_port = acellInfo->hostAddr[0].sin_port;
1431 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
1432                         hostAddr[numServers].sin_len = sizeof(struct sockaddr_in);
1433 #endif
1434                         memcpy(&hostAddr[numServers].sin_addr.s_addr, he->h_addr_list[i], sizeof(long));
1435                         strcpy(hostName[numServers], acellInfo->hostName[j]);
1436                         foundAddr = 1;
1437                         numServers++;
1438                     }
1439                 }
1440                 if (!foundAddr) {
1441                     hostAddr[numServers] = acellInfo->hostAddr[j];
1442                     strcpy(hostName[numServers], acellInfo->hostName[j]);
1443                     numServers++;
1444                 }
1445             }
1446
1447             for (i=0; i<numServers; i++) {
1448                 acellInfo->hostAddr[i] = hostAddr[i];
1449                 strcpy(acellInfo->hostName[i], hostName[i]);
1450             }
1451             acellInfo->numServers = numServers;
1452             acellInfo->flags |= AFSCONF_CELL_FLAG_DNS_QUERIED;
1453         }
1454         UNLOCK_GLOBAL_MUTEX;
1455         return 0;
1456     } else {
1457         UNLOCK_GLOBAL_MUTEX;
1458         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1459     }
1460 }
1461
1462 int
1463 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1464                      afs_int32 alen)
1465 {
1466     static int afsconf_showcell = 0;
1467     char *afscell_path;
1468     afs_int32 code = 0;
1469
1470     LOCK_GLOBAL_MUTEX;
1471     /*
1472      * If a cell switch was specified in a command, then it should override the 
1473      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1474      * is set and the cell name in the adir structure is used.
1475      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1476      */
1477     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1478         if (!afsconf_showcell) {
1479             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1480                     afscell_path);
1481             afsconf_showcell = 1;
1482         }
1483         strncpy(aname, afscell_path, alen);
1484     } else {
1485         afsconf_Check(adir);
1486         if (adir->cellName) {
1487             strncpy(aname, adir->cellName, alen);
1488         } else
1489             code = AFSCONF_UNKNOWN;
1490     }
1491
1492     UNLOCK_GLOBAL_MUTEX;
1493     return (code);
1494 }
1495
1496 int
1497 afsconf_Close(struct afsconf_dir *adir)
1498 {
1499     LOCK_GLOBAL_MUTEX;
1500     afsconf_CloseInternal(adir);
1501     if (adir->name)
1502         free(adir->name);
1503     free(adir);
1504     UNLOCK_GLOBAL_MUTEX;
1505     return 0;
1506 }
1507
1508 static int
1509 afsconf_CloseInternal(register struct afsconf_dir *adir)
1510 {
1511     register struct afsconf_entry *td, *nd;
1512     struct afsconf_aliasentry *ta, *na;
1513     register char *tname;
1514
1515     tname = adir->name;         /* remember name, since that's all we preserve */
1516
1517     /* free everything we can find */
1518     if (adir->cellName)
1519         free(adir->cellName);
1520     for (td = adir->entries; td; td = nd) {
1521         nd = td->next;
1522         if (td->cellInfo.linkedCell)
1523             free(td->cellInfo.linkedCell);
1524         free(td);
1525     }
1526     for (ta = adir->alias_entries; ta; ta = na) {
1527         na = ta->next;
1528         free(ta);
1529     }
1530     if (adir->keystr)
1531         free(adir->keystr);
1532
1533     /* reinit */
1534     memset(adir, 0, sizeof(struct afsconf_dir));
1535     adir->name = tname;         /* restore it */
1536     return 0;
1537 }
1538
1539 static int
1540 afsconf_Reopen(register struct afsconf_dir *adir)
1541 {
1542     register afs_int32 code;
1543     code = afsconf_CloseInternal(adir);
1544     if (code)
1545         return code;
1546     code = afsconf_OpenInternal(adir, 0, 0);
1547     return code;
1548 }
1549
1550 /* called during opening of config file */
1551 int
1552 afsconf_IntGetKeys(struct afsconf_dir *adir)
1553 {
1554     char tbuffer[256];
1555     register int fd;
1556     struct afsconf_keys *tstr;
1557     register afs_int32 code;
1558
1559 #ifdef AFS_NT40_ENV
1560     /* NT client config dir has no KeyFile; don't risk attempting open
1561      * because there might be a random file of this name if dir is shared.
1562      */
1563     if (IsClientConfigDirectory(adir->name)) {
1564         adir->keystr = ((struct afsconf_keys *)
1565                         malloc(sizeof(struct afsconf_keys)));
1566         adir->keystr->nkeys = 0;
1567         return 0;
1568     }
1569 #endif /* AFS_NT40_ENV */
1570
1571     LOCK_GLOBAL_MUTEX;
1572     /* compute the key name and other setup */
1573     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1574     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1575     adir->keystr = tstr;
1576
1577     /* read key file */
1578     fd = open(tbuffer, O_RDONLY);
1579     if (fd < 0) {
1580         tstr->nkeys = 0;
1581         UNLOCK_GLOBAL_MUTEX;
1582         return 0;
1583     }
1584     code = read(fd, tstr, sizeof(struct afsconf_keys));
1585     close(fd);
1586     if (code < sizeof(afs_int32)) {
1587         tstr->nkeys = 0;
1588         UNLOCK_GLOBAL_MUTEX;
1589         return 0;
1590     }
1591
1592     /* convert key structure to host order */
1593     tstr->nkeys = ntohl(tstr->nkeys);
1594
1595     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1596         tstr->nkeys = 0;
1597         UNLOCK_GLOBAL_MUTEX;
1598         return 0;
1599     }
1600
1601     for (fd = 0; fd < tstr->nkeys; fd++)
1602         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1603
1604     UNLOCK_GLOBAL_MUTEX;
1605     return 0;
1606 }
1607
1608 /* get keys structure */
1609 int
1610 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1611 {
1612     register afs_int32 code;
1613
1614     LOCK_GLOBAL_MUTEX;
1615     code = afsconf_Check(adir);
1616     if (code) {
1617         UNLOCK_GLOBAL_MUTEX;
1618         return AFSCONF_FAILURE;
1619     }
1620     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1621     UNLOCK_GLOBAL_MUTEX;
1622     return 0;
1623 }
1624
1625 /* get latest key */
1626 afs_int32
1627 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1628                      struct ktc_encryptionKey *akey)
1629 {
1630     register int i;
1631     int maxa;
1632     register struct afsconf_key *tk;
1633     register afs_int32 best;
1634     struct afsconf_key *bestk;
1635     register afs_int32 code;
1636
1637     LOCK_GLOBAL_MUTEX;
1638     code = afsconf_Check(adir);
1639     if (code) {
1640         UNLOCK_GLOBAL_MUTEX;
1641         return AFSCONF_FAILURE;
1642     }
1643     maxa = adir->keystr->nkeys;
1644
1645     best = -1;                  /* highest kvno we've seen yet */
1646     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1647     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1648         if (tk->kvno == 999)
1649             continue;           /* skip bcrypt keys */
1650         if (tk->kvno > best) {
1651             best = tk->kvno;
1652             bestk = tk;
1653         }
1654     }
1655     if (bestk) {                /* found any  */
1656         if (akey)
1657             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1658         if (avno)
1659             *avno = bestk->kvno;        /* and kvno to caller */
1660         UNLOCK_GLOBAL_MUTEX;
1661         return 0;
1662     }
1663     UNLOCK_GLOBAL_MUTEX;
1664     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1665 }
1666
1667 /* get a particular key */
1668 int
1669 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1670 {
1671     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1672     register int i, maxa;
1673     register struct afsconf_key *tk;
1674     register afs_int32 code;
1675
1676     LOCK_GLOBAL_MUTEX;
1677     code = afsconf_Check(adir);
1678     if (code) {
1679         UNLOCK_GLOBAL_MUTEX;
1680         return AFSCONF_FAILURE;
1681     }
1682     maxa = adir->keystr->nkeys;
1683
1684     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1685         if (tk->kvno == avno) {
1686             memcpy(akey, tk->key, 8);
1687             UNLOCK_GLOBAL_MUTEX;
1688             return 0;
1689         }
1690     }
1691
1692     UNLOCK_GLOBAL_MUTEX;
1693     return AFSCONF_NOTFOUND;
1694 }
1695
1696 /* save the key structure in the appropriate file */
1697 static int
1698 SaveKeys(struct afsconf_dir *adir)
1699 {
1700     struct afsconf_keys tkeys;
1701     register int fd;
1702     register afs_int32 i;
1703     char tbuffer[256];
1704
1705     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1706
1707     /* convert it to net byte order */
1708     for (i = 0; i < tkeys.nkeys; i++)
1709         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1710     tkeys.nkeys = htonl(tkeys.nkeys);
1711
1712     /* rewrite keys file */
1713     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1714     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1715     if (fd < 0)
1716         return AFSCONF_FAILURE;
1717     i = write(fd, &tkeys, sizeof(tkeys));
1718     if (i != sizeof(tkeys)) {
1719         close(fd);
1720         return AFSCONF_FAILURE;
1721     }
1722     if (close(fd) < 0)
1723         return AFSCONF_FAILURE;
1724     return 0;
1725 }
1726
1727 int
1728 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1729                afs_int32 overwrite)
1730 {
1731     register struct afsconf_keys *tk;
1732     register struct afsconf_key *tkey;
1733     register afs_int32 i;
1734     int foundSlot;
1735
1736     LOCK_GLOBAL_MUTEX;
1737     tk = adir->keystr;
1738
1739     if (akvno != 999) {
1740         if (akvno < 0 || akvno > 255) {
1741             UNLOCK_GLOBAL_MUTEX;
1742             return ERANGE;
1743         }
1744     }
1745     foundSlot = 0;
1746     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1747         if (tkey->kvno == akvno) {
1748             if (!overwrite) {
1749                 UNLOCK_GLOBAL_MUTEX;
1750                 return AFSCONF_KEYINUSE;
1751             }
1752             foundSlot = 1;
1753             break;
1754         }
1755     }
1756     if (!foundSlot) {
1757         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1758             UNLOCK_GLOBAL_MUTEX;
1759             return AFSCONF_FULL;
1760         }
1761         tkey = &tk->key[tk->nkeys++];
1762     }
1763     tkey->kvno = akvno;
1764     memcpy(tkey->key, akey, 8);
1765     i = SaveKeys(adir);
1766     afsconf_Touch(adir);
1767     UNLOCK_GLOBAL_MUTEX;
1768     return i;
1769 }
1770
1771 /* this proc works by sliding the other guys down, rather than using a funny
1772     kvno value, so that callers can count on getting a good key in key[0].
1773 */
1774 int
1775 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1776 {
1777     register struct afsconf_keys *tk;
1778     register struct afsconf_key *tkey;
1779     register int i;
1780     int foundFlag = 0;
1781
1782     LOCK_GLOBAL_MUTEX;
1783     tk = adir->keystr;
1784
1785     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1786         if (tkey->kvno == akvno) {
1787             foundFlag = 1;
1788             break;
1789         }
1790     }
1791     if (!foundFlag) {
1792         UNLOCK_GLOBAL_MUTEX;
1793         return AFSCONF_NOTFOUND;
1794     }
1795
1796     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1797     for (; i < tk->nkeys - 1; i++, tkey++) {
1798         tkey->kvno = (tkey + 1)->kvno;
1799         memcpy(tkey->key, (tkey + 1)->key, 8);
1800     }
1801     tk->nkeys--;
1802     i = SaveKeys(adir);
1803     afsconf_Touch(adir);
1804     UNLOCK_GLOBAL_MUTEX;
1805     return i;
1806 }