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