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