1/*-
2 * Copyright (c) 1985, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1985, 1993\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#if 0
37#ifndef lint
38static char sccsid[] = "@(#)timed.c	8.1 (Berkeley) 6/6/93";
39#endif /* not lint */
40#endif
41
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD$");
44
45#include "globals.h"
46#include <net/if.h>
47#include <sys/file.h>
48#include <sys/ioctl.h>
49#include <setjmp.h>
50#include "pathnames.h"
51#include <math.h>
52#include <sys/types.h>
53#include <sys/times.h>
54
55int trace = 0;
56int sock, sock_raw = -1;
57int status = 0;
58u_short sequence;			/* sequence number */
59long delay1;
60long delay2;
61
62int nslavenets;				/* nets were I could be a slave */
63int nmasternets;			/* nets were I could be a master */
64int nignorednets;			/* ignored nets */
65int nnets;				/* nets I am connected to */
66
67FILE *fd;				/* trace file FD */
68
69jmp_buf jmpenv;
70
71struct netinfo *nettab = NULL;
72struct netinfo *slavenet;
73int Mflag;
74int justquit = 0;
75int debug;
76
77static struct nets {
78	char	*name;
79	long	net;
80	struct nets *next;
81} *nets = NULL;
82
83struct hosttbl hosttbl[NHOSTS+1];	/* known hosts */
84
85static struct goodhost {		/* hosts that we trust */
86	char	name[MAXHOSTNAMELEN];
87	struct goodhost *next;
88	char	perm;
89} *goodhosts;
90
91static char *goodgroup;			/* net group of trusted hosts */
92static void checkignorednets(void);
93static void pickslavenet(struct netinfo *);
94static void add_good_host(char *, int);
95static void usage(void);
96
97/*
98 * The timedaemons synchronize the clocks of hosts in a local area network.
99 * One daemon runs as master, all the others as slaves. The master
100 * performs the task of computing clock differences and sends correction
101 * values to the slaves.
102 * Slaves start an election to choose a new master when the latter disappears
103 * because of a machine crash, network partition, or when killed.
104 * A resolution protocol is used to kill all but one of the masters
105 * that happen to exist in segments of a partitioned network when the
106 * network partition is fixed.
107 *
108 * Authors: Riccardo Gusella & Stefano Zatti
109 *
110 * overhauled at Silicon Graphics
111 */
112int
113main(int argc, char *argv[])
114{
115	int on;
116	int ret;
117	int nflag, iflag;
118	struct timeval ntime;
119	struct servent *srvp;
120	char buf[BUFSIZ], *cp, *cplim;
121	struct ifconf ifc;
122	struct ifreq ifreq, ifreqf, *ifr;
123	register struct netinfo *ntp;
124	struct netinfo *ntip;
125	struct netinfo *savefromnet;
126	struct netent *nentp;
127	struct nets *nt;
128	struct sockaddr_in server;
129	u_short port;
130	int c;
131
132#ifdef lint
133	ntip = NULL;
134#endif
135
136	on = 1;
137	nflag = OFF;
138	iflag = OFF;
139
140
141	opterr = 0;
142	while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
143		switch (c) {
144		case 'M':
145			Mflag = 1;
146			break;
147
148		case 't':
149			trace = 1;
150			break;
151
152		case 'n':
153			if (iflag) {
154				errx(1, "-i and -n make no sense together");
155			} else {
156				nflag = ON;
157				addnetname(optarg);
158			}
159			break;
160
161		case 'i':
162			if (nflag) {
163				errx(1, "-i and -n make no sense together");
164			} else {
165				iflag = ON;
166				addnetname(optarg);
167			}
168			break;
169
170		case 'F':
171			add_good_host(optarg,1);
172			while (optind < argc && argv[optind][0] != '-')
173				add_good_host(argv[optind++], 1);
174			break;
175
176		case 'd':
177			debug = 1;
178			break;
179		case 'G':
180			if (goodgroup != NULL)
181				errx(1, "only one net group");
182			goodgroup = optarg;
183			break;
184
185		default:
186			usage();
187			break;
188		}
189	}
190	if (optind < argc)
191		usage();
192
193	/* If we care about which machine is the master, then we must
194	 *	be willing to be a master
195	 */
196	if (goodgroup != NULL || goodhosts != NULL)
197		Mflag = 1;
198
199	if (gethostname(hostname, sizeof(hostname) - 1) < 0)
200		err(1, "gethostname");
201	self.l_bak = &self;
202	self.l_fwd = &self;
203	self.h_bak = &self;
204	self.h_fwd = &self;
205	self.head = 1;
206	self.good = 1;
207
208	if (goodhosts != NULL)		/* trust ourself */
209		add_good_host(hostname,1);
210
211	srvp = getservbyname("timed", "udp");
212	if (srvp == NULL)
213		errx(1, "timed/udp: unknown service");
214	port = srvp->s_port;
215	bzero(&server, sizeof(struct sockaddr_in));
216	server.sin_port = srvp->s_port;
217	server.sin_family = AF_INET;
218	sock = socket(AF_INET, SOCK_DGRAM, 0);
219	if (sock < 0)
220		err(1, "socket");
221	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
222							sizeof(on)) < 0)
223		err(1, "setsockopt");
224	if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
225		if (errno == EADDRINUSE)
226			warnx("time daemon already running");
227		else
228			warn("bind");
229		exit(1);
230	}
231
232	sequence = arc4random();     /* initial seq number */
233
234	(void)gettimeofday(&ntime, NULL);
235	/* rounds kernel variable time to multiple of 5 ms. */
236	ntime.tv_sec = 0;
237	ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
238	(void)adjtime(&ntime, (struct timeval *)0);
239
240	for (nt = nets; nt; nt = nt->next) {
241		nentp = getnetbyname(nt->name);
242		if (nentp == NULL) {
243			nt->net = inet_network(nt->name);
244			if (nt->net != INADDR_NONE)
245				nentp = getnetbyaddr(nt->net, AF_INET);
246		}
247		if (nentp != NULL) {
248			nt->net = nentp->n_net;
249		} else if (nt->net == INADDR_NONE) {
250			errx(1, "unknown net %s", nt->name);
251		} else if (nt->net == INADDR_ANY) {
252			errx(1, "bad net %s", nt->name);
253		} else {
254			warnx("warning: %s unknown in /etc/networks",
255				nt->name);
256		}
257
258		if (0 == (nt->net & 0xff000000))
259		    nt->net <<= 8;
260		if (0 == (nt->net & 0xff000000))
261		    nt->net <<= 8;
262		if (0 == (nt->net & 0xff000000))
263		    nt->net <<= 8;
264	}
265	ifc.ifc_len = sizeof(buf);
266	ifc.ifc_buf = buf;
267	if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
268		err(1, "get interface configuration");
269	ntp = NULL;
270#define size(p)	max((p).sa_len, sizeof(p))
271	cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
272	for (cp = buf; cp < cplim;
273			cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
274		ifr = (struct ifreq *)cp;
275		if (ifr->ifr_addr.sa_family != AF_INET)
276			continue;
277		if (!ntp)
278			ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
279		bzero(ntp,sizeof(*ntp));
280		ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
281		ntp->status = NOMASTER;
282		ifreq = *ifr;
283		ifreqf = *ifr;
284
285		if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
286			warn("get interface flags");
287			continue;
288		}
289		if ((ifreqf.ifr_flags & IFF_UP) == 0)
290			continue;
291		if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
292		    (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
293			continue;
294		}
295
296
297		if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
298			warn("get netmask");
299			continue;
300		}
301		ntp->mask = ((struct sockaddr_in *)
302			&ifreq.ifr_addr)->sin_addr.s_addr;
303
304		if (ifreqf.ifr_flags & IFF_BROADCAST) {
305			if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
306				warn("get broadaddr");
307				continue;
308			}
309			ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
310			/* What if the broadcast address is all ones?
311			 * So we cannot just mask ntp->dest_addr.  */
312			ntp->net = ntp->my_addr;
313			ntp->net.s_addr &= ntp->mask;
314		} else {
315			if (ioctl(sock, SIOCGIFDSTADDR,
316						(char *)&ifreq) < 0) {
317				warn("get destaddr");
318				continue;
319			}
320			ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
321			ntp->net = ntp->dest_addr.sin_addr;
322		}
323
324		ntp->dest_addr.sin_port = port;
325
326		for (nt = nets; nt; nt = nt->next) {
327			if (ntp->net.s_addr == htonl(nt->net))
328				break;
329		}
330		if ((nflag && !nt) || (iflag && nt))
331			continue;
332
333		ntp->next = NULL;
334		if (nettab == NULL) {
335			nettab = ntp;
336		} else {
337			ntip->next = ntp;
338		}
339		ntip = ntp;
340		ntp = NULL;
341	}
342	if (ntp)
343		(void) free((char *)ntp);
344	if (nettab == NULL)
345		errx(1, "no network usable");
346
347	/* microseconds to delay before responding to a broadcast */
348	delay1 = casual(1, 100*1000);
349
350	/* election timer delay in secs. */
351	delay2 = casual(MINTOUT, MAXTOUT);
352
353	if (!debug)
354		daemon(debug, 0);
355
356	if (trace)
357		traceon();
358	openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
359
360	/*
361	 * keep returning here
362	 */
363	ret = setjmp(jmpenv);
364	savefromnet = fromnet;
365	setstatus();
366
367	if (Mflag) {
368		switch (ret) {
369
370		case 0:
371			checkignorednets();
372			pickslavenet(0);
373			break;
374		case 1:
375			/* Just lost our master */
376			if (slavenet != NULL)
377				slavenet->status = election(slavenet);
378			if (!slavenet || slavenet->status == MASTER) {
379				checkignorednets();
380				pickslavenet(0);
381			} else {
382				makeslave(slavenet);	/* prune extras */
383			}
384			break;
385
386		case 2:
387			/* Just been told to quit */
388			justquit = 1;
389			pickslavenet(savefromnet);
390			break;
391		}
392
393		setstatus();
394		if (!(status & MASTER) && sock_raw != -1) {
395			/* sock_raw is not being used now */
396			(void)close(sock_raw);
397			sock_raw = -1;
398		}
399
400		if (status == MASTER)
401			master();
402		else
403			slave();
404
405	} else {
406		if (sock_raw != -1) {
407			(void)close(sock_raw);
408			sock_raw = -1;
409		}
410
411		if (ret) {
412			/* we just lost our master or were told to quit */
413			justquit = 1;
414		}
415		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
416			if (ntp->status == MASTER) {
417				rmnetmachs(ntp);
418				ntp->status = NOMASTER;
419			}
420		}
421		checkignorednets();
422		pickslavenet(0);
423		setstatus();
424
425		slave();
426	}
427	/* NOTREACHED */
428	return(0);
429}
430
431static void
432usage(void)
433{
434#ifdef HAVENIS
435	fprintf(stderr,
436"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
437#else
438	fprintf(stderr,
439"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
440#endif /* HAVENIS */
441	exit(1);
442}
443
444/*
445 * suppress an upstart, untrustworthy, self-appointed master
446 */
447void
448suppress(struct sockaddr_in *addr, char *name, struct netinfo *net)
449{
450	struct sockaddr_in tgt;
451	char tname[MAXHOSTNAMELEN];
452	struct tsp msg;
453	static struct timeval wait;
454
455	if (trace)
456		fprintf(fd, "suppress: %s\n", name);
457	tgt = *addr;
458	(void)strlcpy(tname, name, sizeof(tname));
459
460	while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
461		if (trace)
462			fprintf(fd, "suppress:\tdiscarded packet from %s\n",
463				    name);
464	}
465
466	syslog(LOG_NOTICE, "suppressing false master %s", tname);
467	msg.tsp_type = TSP_QUIT;
468	(void)strcpy(msg.tsp_name, hostname);
469	(void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
470}
471
472void
473lookformaster(struct netinfo *ntp)
474{
475	struct tsp resp, conflict, *answer;
476	struct timeval ntime;
477	char mastername[MAXHOSTNAMELEN];
478	struct sockaddr_in masteraddr;
479
480	get_goodgroup(0);
481	ntp->status = SLAVE;
482
483	/* look for master */
484	resp.tsp_type = TSP_MASTERREQ;
485	(void)strcpy(resp.tsp_name, hostname);
486	answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
487			 TSP_MASTERACK, ntp, 0);
488	if (answer != NULL && !good_host_name(answer->tsp_name)) {
489		suppress(&from, answer->tsp_name, ntp);
490		ntp->status = NOMASTER;
491		answer = NULL;
492	}
493	if (answer == NULL) {
494		/*
495		 * Various conditions can cause conflict: races between
496		 * two just started timedaemons when no master is
497		 * present, or timedaemons started during an election.
498		 * A conservative approach is taken.  Give up and became a
499		 * slave, postponing election of a master until first
500		 * timer expires.
501		 */
502		ntime.tv_sec = ntime.tv_usec = 0;
503		answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
504		if (answer != NULL) {
505			if (!good_host_name(answer->tsp_name)) {
506				suppress(&from, answer->tsp_name, ntp);
507				ntp->status = NOMASTER;
508			}
509			return;
510		}
511
512		ntime.tv_sec = ntime.tv_usec = 0;
513		answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
514		if (answer != NULL) {
515			if (!good_host_name(answer->tsp_name)) {
516				suppress(&from, answer->tsp_name, ntp);
517				ntp->status = NOMASTER;
518			}
519			return;
520		}
521
522		ntime.tv_sec = ntime.tv_usec = 0;
523		answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
524		if (answer != NULL) {
525			if (!good_host_name(answer->tsp_name)) {
526				suppress(&from, answer->tsp_name, ntp);
527				ntp->status = NOMASTER;
528			}
529			return;
530		}
531
532		if (Mflag)
533			ntp->status = MASTER;
534		else
535			ntp->status = NOMASTER;
536		return;
537	}
538
539	ntp->status = SLAVE;
540	(void)strcpy(mastername, answer->tsp_name);
541	masteraddr = from;
542
543	/*
544	 * If network has been partitioned, there might be other
545	 * masters; tell the one we have just acknowledged that
546	 * it has to gain control over the others.
547	 */
548	ntime.tv_sec = 0;
549	ntime.tv_usec = 300000;
550	answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
551	/*
552	 * checking also not to send CONFLICT to ack'ed master
553	 * due to duplicated MASTERACKs
554	 */
555	if (answer != NULL &&
556	    strcmp(answer->tsp_name, mastername) != 0) {
557		conflict.tsp_type = TSP_CONFLICT;
558		(void)strcpy(conflict.tsp_name, hostname);
559		if (!acksend(&conflict, &masteraddr, mastername,
560			     TSP_ACK, 0, 0)) {
561			syslog(LOG_ERR,
562			       "error on sending TSP_CONFLICT");
563		}
564	}
565}
566
567/*
568 * based on the current network configuration, set the status, and count
569 * networks;
570 */
571void
572setstatus(void)
573{
574	struct netinfo *ntp;
575
576	status = 0;
577	nmasternets = nslavenets = nnets = nignorednets = 0;
578	if (trace)
579		fprintf(fd, "Net status:\n");
580	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
581		switch ((int)ntp->status) {
582		case MASTER:
583			nmasternets++;
584			break;
585		case SLAVE:
586			nslavenets++;
587			break;
588		case NOMASTER:
589		case IGNORE:
590			nignorednets++;
591			break;
592		}
593		if (trace) {
594			fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
595			switch ((int)ntp->status) {
596			case NOMASTER:
597				fprintf(fd, "NOMASTER\n");
598				break;
599			case MASTER:
600				fprintf(fd, "MASTER\n");
601				break;
602			case SLAVE:
603				fprintf(fd, "SLAVE\n");
604				break;
605			case IGNORE:
606				fprintf(fd, "IGNORE\n");
607				break;
608			default:
609				fprintf(fd, "invalid state %d\n",
610					(int)ntp->status);
611				break;
612			}
613		}
614		nnets++;
615		status |= ntp->status;
616	}
617	status &= ~IGNORE;
618	if (trace)
619		fprintf(fd,
620		    "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
621		    nnets, nmasternets, nslavenets, nignorednets, delay2);
622}
623
624void
625makeslave(struct netinfo *net)
626{
627	register struct netinfo *ntp;
628
629	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
630		if (ntp->status == SLAVE && ntp != net)
631			ntp->status = IGNORE;
632	}
633	slavenet = net;
634}
635
636/*
637 * Try to become master over ignored nets..
638 */
639static void
640checkignorednets(void)
641{
642	register struct netinfo *ntp;
643
644	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
645		if (!Mflag && ntp->status == SLAVE)
646			break;
647
648		if (ntp->status == IGNORE || ntp->status == NOMASTER) {
649			lookformaster(ntp);
650			if (!Mflag && ntp->status == SLAVE)
651				break;
652		}
653	}
654}
655
656/*
657 * choose a good network on which to be a slave
658 *	The ignored networks must have already been checked.
659 *	Take a hint about for a good network.
660 */
661static void
662pickslavenet(struct netinfo *ntp)
663{
664	if (slavenet != NULL && slavenet->status == SLAVE) {
665		makeslave(slavenet);		/* prune extras */
666		return;
667	}
668
669	if (ntp == NULL || ntp->status != SLAVE) {
670		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
671			if (ntp->status == SLAVE)
672				break;
673		}
674	}
675	makeslave(ntp);
676}
677
678/*
679 * returns a random number in the range [inf, sup]
680 */
681long
682casual(long inf, long sup)
683{
684	double value;
685
686	value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
687	return(inf + (sup - inf)*value);
688}
689
690char *
691date(void)
692{
693	time_t	tv_sec;
694
695	tv_sec = time(NULL);
696	return (ctime(&tv_sec));
697}
698
699void
700addnetname(char *name)
701{
702	register struct nets **netlist = &nets;
703
704	while (*netlist)
705		netlist = &((*netlist)->next);
706	*netlist = (struct nets *)malloc(sizeof **netlist);
707	if (*netlist == NULL)
708		errx(1, "malloc failed");
709	bzero((char *)*netlist, sizeof(**netlist));
710	(*netlist)->name = name;
711}
712
713/* note a host as trustworthy
714 * perm		1=not part of the netgroup
715 */
716static void
717add_good_host(char *name, int perm)
718{
719	register struct goodhost *ghp;
720	register struct hostent *hentp;
721
722	ghp = (struct goodhost*)malloc(sizeof(*ghp));
723	if (!ghp) {
724		syslog(LOG_ERR, "malloc failed");
725		exit(1);
726	}
727
728	bzero((char*)ghp, sizeof(*ghp));
729	(void)strncpy(&ghp->name[0], name, sizeof(ghp->name));
730	ghp->next = goodhosts;
731	ghp->perm = perm;
732	goodhosts = ghp;
733
734	hentp = gethostbyname(name);
735	if (hentp == NULL && perm)
736		warnx("unknown host %s", name);
737}
738
739
740/* update our image of the net-group of trustworthy hosts
741 */
742void
743get_goodgroup(int force)
744{
745# define NG_DELAY (30*60*CLK_TCK)	/* 30 minutes */
746	static unsigned long last_update = -NG_DELAY;
747	unsigned long new_update;
748	struct goodhost *ghp, **ghpp;
749#ifdef HAVENIS
750	struct hosttbl *htp;
751	char *mach, *usr, *dom;
752#endif /* HAVENIS */
753	struct tms tm;
754
755
756	/* if no netgroup, then we are finished */
757	if (goodgroup == NULL || !Mflag)
758		return;
759
760	/* Do not chatter with the netgroup master too often.
761	 */
762	new_update = times(&tm);
763	if (new_update < last_update + NG_DELAY
764	    && !force)
765		return;
766	last_update = new_update;
767
768	/* forget the old temporary entries */
769	ghpp = &goodhosts;
770	while ((ghp = *ghpp) != NULL) {
771		if (!ghp->perm) {
772			*ghpp = ghp->next;
773			free(ghp);
774		} else {
775			ghpp = &ghp->next;
776		}
777	}
778
779#ifdef HAVENIS
780	/* quit now if we are not one of the trusted masters
781	 */
782	if (!innetgr(goodgroup, &hostname[0], 0,0)) {
783		if (trace)
784			(void)fprintf(fd, "get_goodgroup: %s not in %s\n",
785				      &hostname[0], goodgroup);
786		return;
787	}
788	if (trace)
789		(void)fprintf(fd, "get_goodgroup: %s in %s\n",
790				  &hostname[0], goodgroup);
791
792	/* mark the entire netgroup as trusted */
793	(void)setnetgrent(goodgroup);
794	while (getnetgrent(&mach,&usr,&dom)) {
795		if (mach != NULL)
796			add_good_host(mach,0);
797	}
798	(void)endnetgrent();
799
800	/* update list of slaves */
801	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
802		htp->good = good_host_name(&htp->name[0]);
803	}
804#endif /* HAVENIS */
805}
806
807
808/* see if a machine is trustworthy
809 */
810int					/* 1=trust hp to change our date */
811good_host_name(char *name)
812{
813	register struct goodhost *ghp = goodhosts;
814	register char c;
815
816	if (!ghp || !Mflag)		/* trust everyone if no one named */
817		return 1;
818
819	c = *name;
820	do {
821		if (c == ghp->name[0]
822		    && !strcasecmp(name, ghp->name))
823			return 1;	/* found him, so say so */
824	} while ((ghp = ghp->next) != NULL);
825
826	if (!strcasecmp(name,hostname))	/* trust ourself */
827		return 1;
828
829	return 0;			/* did not find him */
830}
831