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