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