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