readmsg.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1985, 1993
5 *	The Regents of the University of California.  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 * 4. 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#ifndef lint
33#if 0
34static char sccsid[] = "@(#)readmsg.c	8.1 (Berkeley) 6/6/93";
35#endif
36static const char rcsid[] =
37  "$FreeBSD: stable/11/usr.sbin/timed/timed/readmsg.c 330897 2018-03-14 03:19:51Z eadler $";
38#endif /* not lint */
39
40#define	TSPTYPES
41#include "globals.h"
42
43/*
44 * LOOKAT checks if the message is of the requested type and comes from
45 * the right machine, returning 1 in case of affirmative answer
46 */
47#define LOOKAT(msg, mtype, mfrom, netp, froms) \
48	(((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) &&		\
49	 ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) &&		\
50	 ((netp) == 0 || 						\
51	  ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr))
52
53struct timeval rtime, rwait, rtout;
54struct tsp msgin;
55static struct tsplist {
56	struct tsp info;
57	struct timeval when;
58	struct sockaddr_in addr;
59	struct tsplist *p;
60} msgslist;
61struct sockaddr_in from;
62struct netinfo *fromnet;
63struct timeval from_when;
64
65/*
66 * `readmsg' returns message `type' sent by `machfrom' if it finds it
67 * either in the receive queue, or in a linked list of previously received
68 * messages that it maintains.
69 * Otherwise it waits to see if the appropriate message arrives within
70 * `intvl' seconds. If not, it returns NULL.
71 */
72
73struct tsp *
74readmsg(int type, char *machfrom, struct timeval *intvl, struct netinfo *netfrom)
75{
76	int length;
77	fd_set ready;
78	static struct tsplist *head = &msgslist;
79	static struct tsplist *tail = &msgslist;
80	static int msgcnt = 0;
81	struct tsplist *prev;
82	register struct netinfo *ntp;
83	register struct tsplist *ptr;
84	ssize_t n;
85
86	if (trace) {
87		fprintf(fd, "readmsg: looking for %s from %s, %s\n",
88			tsptype[type], machfrom == NULL ? "ANY" : machfrom,
89			netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net));
90		if (head->p != NULL) {
91			length = 1;
92			for (ptr = head->p; ptr != NULL; ptr = ptr->p) {
93				/* do not repeat the hundreds of messages */
94				if (++length > 3) {
95					if (ptr == tail) {
96						fprintf(fd,"\t ...%d skipped\n",
97							length);
98					} else {
99						continue;
100					}
101				}
102				fprintf(fd, length > 1 ? "\t" : "queue:\t");
103				print(&ptr->info, &ptr->addr);
104			}
105		}
106	}
107
108	ptr = head->p;
109	prev = head;
110
111	/*
112	 * Look for the requested message scanning through the
113	 * linked list. If found, return it and free the space
114	 */
115
116	while (ptr != NULL) {
117		if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
118again:
119			msgin = ptr->info;
120			from = ptr->addr;
121			from_when = ptr->when;
122			prev->p = ptr->p;
123			if (ptr == tail)
124				tail = prev;
125			free((char *)ptr);
126			fromnet = NULL;
127			if (netfrom == NULL)
128			    for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
129				    if ((ntp->mask & from.sin_addr.s_addr) ==
130					ntp->net.s_addr) {
131					    fromnet = ntp;
132					    break;
133				    }
134			    }
135			else
136			    fromnet = netfrom;
137			if (trace) {
138				fprintf(fd, "readmsg: found ");
139				print(&msgin, &from);
140			}
141
142/* The protocol can get far behind.  When it does, it gets
143 *	hopelessly confused.  So delete duplicate messages.
144 */
145			for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) {
146				if (ptr->addr.sin_addr.s_addr
147					== from.sin_addr.s_addr
148				    && ptr->info.tsp_type == msgin.tsp_type) {
149					if (trace)
150						fprintf(fd, "\tdup ");
151					goto again;
152				}
153			}
154			msgcnt--;
155			return(&msgin);
156		} else {
157			prev = ptr;
158			ptr = ptr->p;
159		}
160	}
161
162	/*
163	 * If the message was not in the linked list, it may still be
164	 * coming from the network. Set the timer and wait
165	 * on a select to read the next incoming message: if it is the
166	 * right one, return it, otherwise insert it in the linked list.
167	 */
168
169	(void)gettimeofday(&rtout, NULL);
170	timevaladd(&rtout, intvl);
171	FD_ZERO(&ready);
172	for (;;) {
173		(void)gettimeofday(&rtime, NULL);
174		timevalsub(&rwait, &rtout, &rtime);
175		if (rwait.tv_sec < 0)
176			rwait.tv_sec = rwait.tv_usec = 0;
177		else if (rwait.tv_sec == 0
178			 && rwait.tv_usec < 1000000/CLK_TCK)
179			rwait.tv_usec = 1000000/CLK_TCK;
180
181		if (trace) {
182			fprintf(fd, "readmsg: wait %jd.%6ld at %s\n",
183				(intmax_t)rwait.tv_sec, rwait.tv_usec, date());
184			/* Notice a full disk, as we flush trace info.
185			 * It is better to flush periodically than at
186			 * every line because the tracing consists of bursts
187			 * of many lines.  Without care, tracing slows
188			 * down the code enough to break the protocol.
189			 */
190			if (rwait.tv_sec != 0
191			    && EOF == fflush(fd))
192				traceoff("Tracing ended for cause at %s\n");
193		}
194
195		FD_SET(sock, &ready);
196		if (!select(sock+1, &ready, (fd_set *)0, (fd_set *)0,
197			   &rwait)) {
198			if (rwait.tv_sec == 0 && rwait.tv_usec == 0)
199				return(0);
200			continue;
201		}
202		length = sizeof(from);
203		if ((n = recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0,
204			     (struct sockaddr*)&from, &length)) < 0) {
205			syslog(LOG_ERR, "recvfrom: %m");
206			exit(1);
207		}
208		/*
209		 * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
210		 * this is still OS-dependent.  Demand that the packet is at
211		 * least long enough to hold a 4.3BSD packet.
212		 */
213		if (n < (ssize_t)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
214			syslog(LOG_NOTICE,
215			    "short packet (%zd/%zu bytes) from %s",
216			      n, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
217			      inet_ntoa(from.sin_addr));
218			continue;
219		}
220		(void)gettimeofday(&from_when, NULL);
221		bytehostorder(&msgin);
222
223		if (msgin.tsp_vers > TSPVERSION) {
224			if (trace) {
225			    fprintf(fd,"readmsg: version mismatch\n");
226			    /* should do a dump of the packet */
227			}
228			continue;
229		}
230
231		if (memchr(msgin.tsp_name,
232		    '\0', sizeof msgin.tsp_name) == NULL) {
233			syslog(LOG_NOTICE, "hostname field not NUL terminated "
234			    "in packet from %s", inet_ntoa(from.sin_addr));
235			continue;
236		}
237
238		fromnet = NULL;
239		for (ntp = nettab; ntp != NULL; ntp = ntp->next)
240			if ((ntp->mask & from.sin_addr.s_addr) ==
241			    ntp->net.s_addr) {
242				fromnet = ntp;
243				break;
244			}
245
246		/*
247		 * drop packets from nets we are ignoring permanently
248		 */
249		if (fromnet == NULL) {
250			/*
251			 * The following messages may originate on
252			 * this host with an ignored network address
253			 */
254			if (msgin.tsp_type != TSP_TRACEON &&
255			    msgin.tsp_type != TSP_SETDATE &&
256			    msgin.tsp_type != TSP_MSITE &&
257			    msgin.tsp_type != TSP_TEST &&
258			    msgin.tsp_type != TSP_TRACEOFF) {
259				if (trace) {
260				    fprintf(fd,"readmsg: discard null net ");
261				    print(&msgin, &from);
262				}
263				continue;
264			}
265		}
266
267		/*
268		 * Throw away messages coming from this machine,
269		 * unless they are of some particular type.
270		 * This gets rid of broadcast messages and reduces
271		 * master processing time.
272		 */
273		if (!strcmp(msgin.tsp_name, hostname)
274		    && msgin.tsp_type != TSP_SETDATE
275		    && msgin.tsp_type != TSP_TEST
276		    && msgin.tsp_type != TSP_MSITE
277		    && msgin.tsp_type != TSP_TRACEON
278		    && msgin.tsp_type != TSP_TRACEOFF
279		    && msgin.tsp_type != TSP_LOOP) {
280			if (trace) {
281				fprintf(fd, "readmsg: discard own ");
282				print(&msgin, &from);
283			}
284			continue;
285		}
286
287		/*
288		 * Send acknowledgements here; this is faster and
289		 * avoids deadlocks that would occur if acks were
290		 * sent from a higher level routine.  Different
291		 * acknowledgements are necessary, depending on
292		 * status.
293		 */
294		if (fromnet == NULL)	/* do not de-reference 0 */
295			ignoreack();
296		else if (fromnet->status == MASTER)
297			masterack();
298		else if (fromnet->status == SLAVE)
299			slaveack();
300		else
301			ignoreack();
302
303		if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
304			if (trace) {
305				fprintf(fd, "readmsg: ");
306				print(&msgin, &from);
307			}
308			return(&msgin);
309		} else if (++msgcnt > NHOSTS*3) {
310
311/* The protocol gets hopelessly confused if it gets too far
312*	behind.  However, it seems able to recover from all cases of lost
313*	packets.  Therefore, if we are swamped, throw everything away.
314*/
315			if (trace)
316				fprintf(fd,
317					"readmsg: discarding %d msgs\n",
318					msgcnt);
319			msgcnt = 0;
320			while ((ptr=head->p) != NULL) {
321				head->p = ptr->p;
322				free((char *)ptr);
323			}
324			tail = head;
325		} else {
326			tail->p = (struct tsplist *)
327				    malloc(sizeof(struct tsplist));
328			tail = tail->p;
329			tail->p = NULL;
330			tail->info = msgin;
331			tail->addr = from;
332			/* timestamp msgs so SETTIMEs are correct */
333			tail->when = from_when;
334		}
335	}
336}
337
338/*
339 * Send the necessary acknowledgements:
340 * only the type ACK is to be sent by a slave
341 */
342void
343slaveack(void)
344{
345	switch(msgin.tsp_type) {
346
347	case TSP_ADJTIME:
348	case TSP_SETTIME:
349	case TSP_ACCEPT:
350	case TSP_REFUSE:
351	case TSP_TRACEON:
352	case TSP_TRACEOFF:
353	case TSP_QUIT:
354		if (trace) {
355			fprintf(fd, "Slaveack: ");
356			print(&msgin, &from);
357		}
358		xmit(TSP_ACK,msgin.tsp_seq, &from);
359		break;
360
361	default:
362		if (trace) {
363			fprintf(fd, "Slaveack: no ack: ");
364			print(&msgin, &from);
365		}
366		break;
367	}
368}
369
370/*
371 * Certain packets may arrive from this machine on ignored networks.
372 * These packets should be acknowledged.
373 */
374void
375ignoreack(void)
376{
377	switch(msgin.tsp_type) {
378
379	case TSP_TRACEON:
380	case TSP_TRACEOFF:
381	case TSP_QUIT:
382		if (trace) {
383			fprintf(fd, "Ignoreack: ");
384			print(&msgin, &from);
385		}
386		xmit(TSP_ACK,msgin.tsp_seq, &from);
387		break;
388
389	default:
390		if (trace) {
391			fprintf(fd, "Ignoreack: no ack: ");
392			print(&msgin, &from);
393		}
394		break;
395	}
396}
397
398/*
399 * `masterack' sends the necessary acknowledgments
400 * to the messages received by a master
401 */
402void
403masterack(void)
404{
405	struct tsp resp;
406
407	resp = msgin;
408	resp.tsp_vers = TSPVERSION;
409	(void)strcpy(resp.tsp_name, hostname);
410
411	switch(msgin.tsp_type) {
412
413	case TSP_QUIT:
414	case TSP_TRACEON:
415	case TSP_TRACEOFF:
416	case TSP_MSITEREQ:
417		if (trace) {
418			fprintf(fd, "Masterack: ");
419			print(&msgin, &from);
420		}
421		xmit(TSP_ACK,msgin.tsp_seq, &from);
422		break;
423
424	case TSP_RESOLVE:
425	case TSP_MASTERREQ:
426		if (trace) {
427			fprintf(fd, "Masterack: ");
428			print(&msgin, &from);
429		}
430		xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
431		break;
432
433	default:
434		if (trace) {
435			fprintf(fd,"Masterack: no ack: ");
436			print(&msgin, &from);
437		}
438		break;
439	}
440}
441
442/*
443 * Print a TSP message
444 */
445void
446print(struct tsp *msg, struct sockaddr_in *addr)
447{
448	char tm[26];
449	time_t tsp_time_sec;
450
451	if (msg->tsp_type >= TSPTYPENUMBER) {
452		fprintf(fd, "bad type (%u) on packet from %s\n",
453		  msg->tsp_type, inet_ntoa(addr->sin_addr));
454		return;
455	}
456
457	switch (msg->tsp_type) {
458
459	case TSP_LOOP:
460		fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
461			tsptype[msg->tsp_type],
462			msg->tsp_vers,
463			msg->tsp_seq,
464			msg->tsp_hopcnt,
465			inet_ntoa(addr->sin_addr),
466			msg->tsp_name);
467		break;
468
469	case TSP_SETTIME:
470	case TSP_SETDATE:
471	case TSP_SETDATEREQ:
472		tsp_time_sec = msg->tsp_time.tv_sec;
473		strncpy(tm, ctime(&tsp_time_sec)+3+1, sizeof(tm));
474		tm[15] = '\0';		/* ugh */
475		fprintf(fd, "%s %d %-6u %s %-15s %s\n",
476			tsptype[msg->tsp_type],
477			msg->tsp_vers,
478			msg->tsp_seq,
479			tm,
480			inet_ntoa(addr->sin_addr),
481			msg->tsp_name);
482		break;
483
484	case TSP_ADJTIME:
485		fprintf(fd, "%s %d %-6u (%d,%d) %-15s %s\n",
486			tsptype[msg->tsp_type],
487			msg->tsp_vers,
488			msg->tsp_seq,
489			msg->tsp_time.tv_sec,
490			msg->tsp_time.tv_usec,
491			inet_ntoa(addr->sin_addr),
492			msg->tsp_name);
493		break;
494
495	default:
496		fprintf(fd, "%s %d %-6u %-15s %s\n",
497			tsptype[msg->tsp_type],
498			msg->tsp_vers,
499			msg->tsp_seq,
500			inet_ntoa(addr->sin_addr),
501			msg->tsp_name);
502		break;
503	}
504}
505