Home | History | Annotate | Line # | Download | only in timed
      1 /*	$NetBSD: timed.c,v 1.31 2024/11/03 10:43:27 rillig 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
     40 static char sccsid[] = "@(#)timed.c	8.2 (Berkeley) 3/26/95";
     41 #else
     42 __RCSID("$NetBSD: timed.c,v 1.31 2024/11/03 10:43:27 rillig 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 
     64 int trace = 0;
     65 int sock, sock_raw = -1;
     66 int status = 0;
     67 u_short sequence;			/* sequence number */
     68 long delay1;
     69 long delay2;
     70 
     71 int nslavenets;				/* nets where I could be a slave */
     72 int nmasternets;			/* nets where I could be a master */
     73 int nignorednets;			/* ignored nets */
     74 int nnets;				/* nets I am connected to */
     75 
     76 FILE *fd;				/* trace file FD */
     77 
     78 jmp_buf jmpenv;
     79 
     80 struct netinfo *nettab = 0;
     81 struct netinfo *slavenet;
     82 int Mflag;
     83 int justquit = 0;
     84 int debug;
     85 
     86 static struct nets {
     87 	char	 *name;
     88 	in_addr_t net;
     89 	struct nets *next;
     90 } *nets = 0;
     91 
     92 struct hosttbl hosttbl[NHOSTS+1];	/* known hosts */
     93 
     94 static struct goodhost {		/* hosts that we trust */
     95 	char	name[MAXHOSTNAMELEN+1];
     96 	struct goodhost *next;
     97 	char	perm;
     98 } *goodhosts;
     99 
    100 static char *goodgroup;			/* net group of trusted hosts */
    101 static void checkignorednets(void);
    102 static void pickslavenet(struct netinfo *);
    103 static 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 
    120 int
    121 main(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 	struct ifaddrs *ifap, *ifa;
    137 
    138 #define	IN_MSG "-i and -n make no sense together\n"
    139 #ifdef HAVENIS
    140 #define USAGE "[-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n"
    141 #else
    142 #define USAGE "[-dtM] [-i net|-n net] [-F host1 host2 ...]\n"
    143 #endif /* HAVENIS */
    144 
    145 	ntip = NULL;
    146 
    147 	on = 1;
    148 	nflag = OFF;
    149 	iflag = OFF;
    150 
    151 	opterr = 0;
    152 	while ((c = getopt(argc, argv, "Mtdn:i:F:G:")) != -1) {
    153 		switch (c) {
    154 		case 'M':
    155 			Mflag = 1;
    156 			break;
    157 
    158 		case 't':
    159 			trace = 1;
    160 			break;
    161 
    162 		case 'n':
    163 			if (iflag)
    164 				errx(EXIT_FAILURE, "%s", IN_MSG);
    165 			nflag = ON;
    166 			addnetname(optarg);
    167 			break;
    168 
    169 		case 'i':
    170 			if (nflag)
    171 				errx(EXIT_FAILURE, "%s", IN_MSG);
    172 			iflag = ON;
    173 			addnetname(optarg);
    174 			break;
    175 
    176 		case 'F':
    177 			add_good_host(optarg,1);
    178 			while (optind < argc && argv[optind][0] != '-')
    179 				add_good_host(argv[optind++], 1);
    180 			break;
    181 
    182 		case 'd':
    183 			debug = 1;
    184 			break;
    185 		case 'G':
    186 			if (goodgroup != 0)
    187 				errx(EXIT_FAILURE, "timed: only one net group");
    188 			goodgroup = optarg;
    189 			break;
    190 		default:
    191 			errx(EXIT_FAILURE, "%s", USAGE);
    192 			break;
    193 		}
    194 	}
    195 	if (optind < argc)
    196 		errx(EXIT_FAILURE, "%s", USAGE);
    197 
    198 	/* If we care about which machine is the master, then we must
    199 	 *	be willing to be a master
    200 	 */
    201 	if (0 != goodgroup || 0 != goodhosts)
    202 		Mflag = 1;
    203 
    204 	if (gethostname(hostname, sizeof(hostname)) < 0)
    205 		err(EXIT_FAILURE, "gethostname");
    206 
    207 	hostname[sizeof(hostname) - 1] = '\0';
    208 	self.l_bak = &self;
    209 	self.l_fwd = &self;
    210 	self.h_bak = &self;
    211 	self.h_fwd = &self;
    212 	self.head = 1;
    213 	self.good = 1;
    214 
    215 	if (goodhosts != 0)		/* trust ourself */
    216 		add_good_host(hostname,1);
    217 
    218 	srvp = getservbyname("timed", "udp");
    219 	if (srvp == NULL)
    220 		errx(EXIT_FAILURE, "unknown service 'timed/udp'");
    221 
    222 	port = srvp->s_port;
    223 	(void)memset(&server, 0, sizeof(server));
    224 	server.sin_port = srvp->s_port;
    225 	server.sin_family = AF_INET;
    226 	sock = socket(AF_INET, SOCK_DGRAM, 0);
    227 	if (sock < 0)
    228 		err(EXIT_FAILURE, "socket");
    229 
    230 	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
    231 		err(EXIT_FAILURE, "setsockopt");
    232 
    233 	if (bind(sock, (struct sockaddr*)(void *)&server, sizeof(server))) {
    234 		if (errno == EADDRINUSE)
    235 			errx(EXIT_FAILURE, "time daemon already running");
    236 		else
    237 			err(EXIT_FAILURE, "bind");
    238 	}
    239 
    240 	/* initial seq number */
    241 	sequence = (u_short)arc4random_uniform(UINT16_MAX);
    242 
    243 	/* rounds kernel variable time to multiple of 5 ms. */
    244 	ntime.tv_sec = 0;
    245 	ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
    246 	(void)adjtime(&ntime, (struct timeval *)0);
    247 
    248 	for (nt = nets; nt; nt = nt->next) {
    249 		nentp = getnetbyname(nt->name);
    250 		if (nentp == 0) {
    251 			nt->net = inet_network(nt->name);
    252 			if (nt->net != INADDR_NONE)
    253 				nentp = getnetbyaddr(nt->net, AF_INET);
    254 		}
    255 		if (nentp != 0)
    256 			nt->net = nentp->n_net;
    257 		else if (nt->net == INADDR_NONE)
    258 			errx(EXIT_FAILURE, "unknown net %s", nt->name);
    259 		else if (nt->net == INADDR_ANY)
    260 			errx(EXIT_FAILURE, "bad net %s", nt->name);
    261 		else
    262 			warnx("warning: %s unknown in /etc/networks",
    263 				nt->name);
    264 
    265 		if (0 == (nt->net & 0xff000000))
    266 		    nt->net <<= 8;
    267 		if (0 == (nt->net & 0xff000000))
    268 		    nt->net <<= 8;
    269 		if (0 == (nt->net & 0xff000000))
    270 		    nt->net <<= 8;
    271 	}
    272 	if (getifaddrs(&ifap) != 0)
    273 		err(EXIT_FAILURE, "get interface configuration");
    274 
    275 	ntp = NULL;
    276 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
    277 		if (ifa->ifa_addr->sa_family != AF_INET)
    278 			continue;
    279 		if (!ntp)
    280 			ntp = malloc(sizeof(struct netinfo));
    281 		(void)memset(ntp, 0, sizeof(*ntp));
    282 		ntp->my_addr=((struct sockaddr_in *)(void *)ifa->ifa_addr)->sin_addr;
    283 		ntp->status = NOMASTER;
    284 
    285 		if ((ifa->ifa_flags & IFF_UP) == 0)
    286 			continue;
    287 		if ((ifa->ifa_flags & IFF_BROADCAST) == 0 &&
    288 		    (ifa->ifa_flags & IFF_POINTOPOINT) == 0) {
    289 			continue;
    290 		}
    291 
    292 		ntp->mask = ((struct sockaddr_in *)(void *)
    293 		    ifa->ifa_netmask)->sin_addr.s_addr;
    294 
    295 		if (ifa->ifa_flags & IFF_BROADCAST) {
    296 			ntp->dest_addr = *(struct sockaddr_in *)(void *)ifa->ifa_broadaddr;
    297 			/* What if the broadcast address is all ones?
    298 			 * So we cannot just mask ntp->dest_addr.  */
    299 			ntp->net = ntp->my_addr;
    300 			ntp->net.s_addr &= ntp->mask;
    301 		} else {
    302 			ntp->dest_addr = *(struct sockaddr_in *)(void *)ifa->ifa_dstaddr;
    303 			ntp->net = ntp->dest_addr.sin_addr;
    304 		}
    305 
    306 		ntp->dest_addr.sin_port = port;
    307 
    308 		for (nt = nets; nt; nt = nt->next) {
    309 			if (ntohl(ntp->net.s_addr) == nt->net)
    310 				break;
    311 		}
    312 		if ((nflag && !nt) || (iflag && nt))
    313 			continue;
    314 
    315 		ntp->next = NULL;
    316 		if (nettab == NULL) {
    317 			nettab = ntp;
    318 		} else {
    319 			ntip->next = ntp;
    320 		}
    321 		ntip = ntp;
    322 		ntp = NULL;
    323 	}
    324 	freeifaddrs(ifap);
    325 	if (ntp)
    326 		(void) free(ntp);
    327 	if (nettab == NULL)
    328 		errx(EXIT_FAILURE, "no network usable");
    329 
    330 
    331 	/* microseconds to delay before responding to a broadcast */
    332 	delay1 = 1L + arc4random_uniform((100 * 1000L) - 1L);
    333 
    334 	/* election timer delay in secs. */
    335 	delay2 = MINTOUT + arc4random_uniform(MAXTOUT - MINTOUT);
    336 
    337 	if (!debug) {
    338 		daemon(debug, 0);
    339 		pidfile(NULL);
    340 	}
    341 
    342 	if (trace)
    343 		traceon();
    344 	openlog("timed", LOG_PID, LOG_DAEMON);
    345 
    346 	/*
    347 	 * keep returning here
    348 	 */
    349 	ret = setjmp(jmpenv);
    350 	savefromnet = fromnet;
    351 	setstatus();
    352 
    353 	if (Mflag) {
    354 		switch (ret) {
    355 
    356 		case 0:
    357 			checkignorednets();
    358 			pickslavenet(0);
    359 			break;
    360 		case 1:
    361 			/* Just lost our master */
    362 			if (slavenet != 0)
    363 				slavenet->status = election(slavenet);
    364 			if (!slavenet || slavenet->status == MASTER) {
    365 				checkignorednets();
    366 				pickslavenet(0);
    367 			} else {
    368 				makeslave(slavenet);	/* prune extras */
    369 			}
    370 			break;
    371 
    372 		case 2:
    373 			/* Just been told to quit */
    374 			justquit = 1;
    375 			pickslavenet(savefromnet);
    376 			break;
    377 		}
    378 
    379 		setstatus();
    380 		if (!(status & MASTER) && sock_raw != -1) {
    381 			/* sock_raw is not being used now */
    382 			(void)close(sock_raw);
    383 			sock_raw = -1;
    384 		}
    385 
    386 		if (status == MASTER)
    387 			master();
    388 		else
    389 			slave();
    390 
    391 	} else {
    392 		if (sock_raw != -1) {
    393 			(void)close(sock_raw);
    394 			sock_raw = -1;
    395 		}
    396 
    397 		if (ret) {
    398 			/* we just lost our master or were told to quit */
    399 			justquit = 1;
    400 		}
    401 		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
    402 			if (ntp->status == MASTER) {
    403 				rmnetmachs(ntp);
    404 				ntp->status = NOMASTER;
    405 			}
    406 		}
    407 		checkignorednets();
    408 		pickslavenet(0);
    409 		setstatus();
    410 
    411 		slave();
    412 	}
    413 	/* NOTREACHED */
    414 	return(0);
    415 }
    416 
    417 
    418 /* suppress an upstart, untrustworthy, self-appointed master
    419  */
    420 void
    421 suppress(struct sockaddr_in *addr, char *name, struct netinfo *net)
    422 {
    423 	struct sockaddr_in tgt;
    424 	char tname[MAXHOSTNAMELEN];
    425 	struct tsp msg;
    426 	static struct timeval wait;
    427 
    428 	if (trace)
    429 		fprintf(fd, "suppress: %s\n", name);
    430 	tgt = *addr;
    431 	(void)strlcpy(tname, name, sizeof(tname));
    432 
    433 	while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
    434 		if (trace)
    435 			fprintf(fd, "suppress:\tdiscarded packet from %s\n",
    436 				    name);
    437 	}
    438 
    439 	syslog(LOG_NOTICE, "suppressing false master %s", tname);
    440 	msg.tsp_type = TSP_QUIT;
    441 	set_tsp_name(&msg, hostname);
    442 	(void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
    443 }
    444 
    445 void
    446 lookformaster(struct netinfo *ntp)
    447 {
    448 	struct tsp resp, conflict, *answer;
    449 	struct timeval ntime;
    450 	char mastername[MAXHOSTNAMELEN];
    451 	struct sockaddr_in masteraddr;
    452 
    453 	get_goodgroup(0);
    454 	ntp->status = SLAVE;
    455 
    456 	/* look for master */
    457 	resp.tsp_type = TSP_MASTERREQ;
    458 	set_tsp_name(&resp, hostname);
    459 	answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
    460 			 TSP_MASTERACK, ntp, 0);
    461 	if (answer != 0 && !good_host_name(answer->tsp_name)) {
    462 		suppress(&from, answer->tsp_name, ntp);
    463 		ntp->status = NOMASTER;
    464 		answer = 0;
    465 	}
    466 	if (answer == 0) {
    467 		/*
    468 		 * Various conditions can cause conflict: races between
    469 		 * two just started timedaemons when no master is
    470 		 * present, or timedaemons started during an election.
    471 		 * A conservative approach is taken.  Give up and become a
    472 		 * slave, postponing election of a master until first
    473 		 * timer expires.
    474 		 */
    475 		ntime.tv_sec = ntime.tv_usec = 0;
    476 		answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
    477 		if (answer != 0) {
    478 			if (!good_host_name(answer->tsp_name)) {
    479 				suppress(&from, answer->tsp_name, ntp);
    480 				ntp->status = NOMASTER;
    481 			}
    482 			return;
    483 		}
    484 
    485 		ntime.tv_sec = ntime.tv_usec = 0;
    486 		answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
    487 		if (answer != 0) {
    488 			if (!good_host_name(answer->tsp_name)) {
    489 				suppress(&from, answer->tsp_name, ntp);
    490 				ntp->status = NOMASTER;
    491 			}
    492 			return;
    493 		}
    494 
    495 		ntime.tv_sec = ntime.tv_usec = 0;
    496 		answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
    497 		if (answer != 0) {
    498 			if (!good_host_name(answer->tsp_name)) {
    499 				suppress(&from, answer->tsp_name, ntp);
    500 				ntp->status = NOMASTER;
    501 			}
    502 			return;
    503 		}
    504 
    505 		if (Mflag)
    506 			ntp->status = MASTER;
    507 		else
    508 			ntp->status = NOMASTER;
    509 		return;
    510 	}
    511 
    512 	ntp->status = SLAVE;
    513 	get_tsp_name(answer, mastername, sizeof(mastername));
    514 	masteraddr = from;
    515 
    516 	/*
    517 	 * If network has been partitioned, there might be other
    518 	 * masters; tell the one we have just acknowledged that
    519 	 * it has to gain control over the others.
    520 	 */
    521 	ntime.tv_sec = 0;
    522 	ntime.tv_usec = 300000;
    523 	answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
    524 	/*
    525 	 * checking also not to send CONFLICT to ack'ed master
    526 	 * due to duplicated MASTERACKs
    527 	 */
    528 	if (answer != NULL &&
    529 	    strcmp(answer->tsp_name, mastername) != 0) {
    530 		conflict.tsp_type = TSP_CONFLICT;
    531 		set_tsp_name(&conflict, hostname);
    532 		if (!acksend(&conflict, &masteraddr, mastername,
    533 			     TSP_ACK, 0, 0)) {
    534 			syslog(LOG_ERR,
    535 			       "error on sending TSP_CONFLICT");
    536 		}
    537 	}
    538 }
    539 
    540 /*
    541  * based on the current network configuration, set the status, and count
    542  * networks;
    543  */
    544 void
    545 setstatus(void)
    546 {
    547 	struct netinfo *ntp;
    548 
    549 	status = 0;
    550 	nmasternets = nslavenets = nnets = nignorednets = 0;
    551 	if (trace)
    552 		fprintf(fd, "Net status:\n");
    553 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
    554 		switch ((int)ntp->status) {
    555 		case MASTER:
    556 			nmasternets++;
    557 			break;
    558 		case SLAVE:
    559 			nslavenets++;
    560 			break;
    561 		case NOMASTER:
    562 		case IGNORE:
    563 			nignorednets++;
    564 			break;
    565 		}
    566 		if (trace) {
    567 			fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
    568 			switch ((int)ntp->status) {
    569 			case NOMASTER:
    570 				fprintf(fd, "NOMASTER\n");
    571 				break;
    572 			case MASTER:
    573 				fprintf(fd, "MASTER\n");
    574 				break;
    575 			case SLAVE:
    576 				fprintf(fd, "SLAVE\n");
    577 				break;
    578 			case IGNORE:
    579 				fprintf(fd, "IGNORE\n");
    580 				break;
    581 			default:
    582 				fprintf(fd, "invalid state %d\n",
    583 					(int)ntp->status);
    584 				break;
    585 			}
    586 		}
    587 		nnets++;
    588 		status |= ntp->status;
    589 	}
    590 	status &= ~IGNORE;
    591 	if (trace)
    592 		fprintf(fd,
    593 		    "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
    594 		    nnets, nmasternets, nslavenets, nignorednets, (long)delay2);
    595 }
    596 
    597 void
    598 makeslave(struct netinfo *net)
    599 {
    600 	struct netinfo *ntp;
    601 
    602 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
    603 		if (ntp->status == SLAVE && ntp != net)
    604 			ntp->status = IGNORE;
    605 	}
    606 	slavenet = net;
    607 }
    608 
    609 /*
    610  * Try to become master over ignored nets..
    611  */
    612 static void
    613 checkignorednets(void)
    614 {
    615 	struct netinfo *ntp;
    616 
    617 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
    618 		if (!Mflag && ntp->status == SLAVE)
    619 			break;
    620 
    621 		if (ntp->status == IGNORE || ntp->status == NOMASTER) {
    622 			lookformaster(ntp);
    623 			if (!Mflag && ntp->status == SLAVE)
    624 				break;
    625 		}
    626 	}
    627 }
    628 
    629 /*
    630  * choose a good network on which to be a slave
    631  *	The ignored networks must have already been checked.
    632  *	Take a hint about for a good network.
    633  */
    634 static void
    635 pickslavenet(struct netinfo *ntp)
    636 {
    637 	if (slavenet != 0 && slavenet->status == SLAVE) {
    638 		makeslave(slavenet);		/* prune extras */
    639 		return;
    640 	}
    641 
    642 	if (ntp == 0 || ntp->status != SLAVE) {
    643 		for (ntp = nettab; ntp != 0; ntp = ntp->next) {
    644 			if (ntp->status == SLAVE)
    645 				break;
    646 		}
    647 	}
    648 	makeslave(ntp);
    649 }
    650 
    651 char *
    652 date(void)
    653 {
    654 	struct	timeval tv;
    655 	time_t t;
    656 
    657 	(void)gettimeofday(&tv, (struct timezone *)0);
    658 	t = tv.tv_sec;
    659 	return (ctime(&t));
    660 }
    661 
    662 void
    663 addnetname(char *name)
    664 {
    665 	struct nets **netlist = &nets;
    666 
    667 	while (*netlist)
    668 		netlist = &((*netlist)->next);
    669 	*netlist = calloc(1, sizeof **netlist);
    670 	if (*netlist == NULL)
    671 		err(EXIT_FAILURE, "malloc failed");
    672 	(*netlist)->name = name;
    673 }
    674 
    675 /* note a host as trustworthy */
    676 static void
    677 add_good_host(const char* name,
    678 	      char perm)		/* 1=not part of the netgroup */
    679 {
    680 	struct goodhost *ghp;
    681 	struct hostent *hentp;
    682 
    683 	ghp = calloc(1, sizeof(*ghp));
    684 	if (!ghp) {
    685 		syslog(LOG_ERR, "malloc failed");
    686 		exit(EXIT_FAILURE);
    687 	}
    688 
    689 	(void)strncpy(&ghp->name[0], name, sizeof(ghp->name) - 1);
    690 	ghp->name[sizeof(ghp->name) - 1] = 0;
    691 	ghp->next = goodhosts;
    692 	ghp->perm = perm;
    693 	goodhosts = ghp;
    694 
    695 	hentp = gethostbyname(name);
    696 	if (NULL == hentp && perm)
    697 		warnx("unknown host %s", name);
    698 }
    699 
    700 
    701 /* update our image of the net-group of trustworthy hosts
    702  */
    703 void
    704 get_goodgroup(int force)
    705 {
    706 # define NG_DELAY (30*60*CLK_TCK)	/* 30 minutes */
    707 	static unsigned long last_update;
    708 	static int firsttime;
    709 	unsigned long new_update;
    710 	struct goodhost *ghp, **ghpp;
    711 #ifdef HAVENIS
    712 	struct hosttbl *htp;
    713 	const char *mach, *usr, *dom;
    714 #endif
    715 	struct tms tm;
    716 
    717 	if (firsttime == 0) {
    718 		last_update = -NG_DELAY;
    719 		firsttime++;
    720 	}
    721 
    722 	/* if no netgroup, then we are finished */
    723 	if (goodgroup == 0 || !Mflag)
    724 		return;
    725 
    726 	/* Do not chatter with the netgroup master too often.
    727 	 */
    728 	new_update = times(&tm);
    729 	if (new_update < last_update + NG_DELAY
    730 	    && !force)
    731 		return;
    732 	last_update = new_update;
    733 
    734 	/* forget the old temporary entries */
    735 	ghpp = &goodhosts;
    736 	while (0 != (ghp = *ghpp)) {
    737 		if (!ghp->perm) {
    738 			*ghpp = ghp->next;
    739 			free(ghp);
    740 		} else {
    741 			ghpp = &ghp->next;
    742 		}
    743 	}
    744 
    745 #ifdef HAVENIS
    746 	/* quit now if we are not one of the trusted masters
    747 	 */
    748 	if (!innetgr(goodgroup, &hostname[0], 0,0)) {
    749 		if (trace)
    750 			(void)fprintf(fd, "get_goodgroup: %s not in %s\n",
    751 				      &hostname[0], goodgroup);
    752 		return;
    753 	}
    754 	if (trace)
    755 		(void)fprintf(fd, "get_goodgroup: %s in %s\n",
    756 				  &hostname[0], goodgroup);
    757 
    758 	/* mark the entire netgroup as trusted */
    759 	(void)setnetgrent(goodgroup);
    760 	while (getnetgrent(&mach,&usr,&dom)) {
    761 		if (0 != mach)
    762 			add_good_host(mach,0);
    763 	}
    764 	(void)endnetgrent();
    765 
    766 	/* update list of slaves */
    767 	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
    768 		htp->good = good_host_name(&htp->name[0]);
    769 	}
    770 #endif /* HAVENIS */
    771 }
    772 
    773 
    774 /* see if a machine is trustworthy
    775  */
    776 int					/* 1=trust hp to change our date */
    777 good_host_name(char *name)
    778 {
    779 	struct goodhost *ghp = goodhosts;
    780 	char c;
    781 
    782 	if (!ghp || !Mflag)		/* trust everyone if no one named */
    783 		return 1;
    784 
    785 	c = *name;
    786 	do {
    787 		if (c == ghp->name[0]
    788 		    && !strcasecmp(name, ghp->name))
    789 			return 1;	/* found him, so say so */
    790 	} while (0 != (ghp = ghp->next));
    791 
    792 	if (!strcasecmp(name,hostname))	/* trust ourself */
    793 		return 1;
    794 
    795 	return 0;			/* did not find him */
    796 }
    797