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