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