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