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