1/*	$NetBSD: readmsg.c,v 1.21 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#if 0
35static char sccsid[] = "@(#)readmsg.c	8.1 (Berkeley) 6/6/93";
36#else
37__RCSID("$NetBSD: readmsg.c,v 1.21 2007/02/04 21:17:01 cbiere Exp $");
38#endif
39#endif /* not lint */
40
41#include "globals.h"
42
43extern const char * const tsptype[];
44
45/*
46 * LOOKAT checks if the message is of the requested type and comes from
47 * the right machine, returning 1 in case of affirmative answer
48 */
49#define LOOKAT(msg, mtype, mfrom, netp, froms) \
50	(((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) &&		\
51	 ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) &&		\
52	 ((netp) == 0 || 						\
53	  ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr))
54
55struct timeval rtime, rwait, rtout;
56struct tsp msgin;
57static struct tsplist {
58	struct tsp info;
59	struct timeval when;
60	struct sockaddr_in addr;
61	struct tsplist *p;
62} msgslist;
63struct sockaddr_in from;
64struct netinfo *fromnet;
65struct timeval from_when;
66
67/*
68 * `readmsg' returns message `type' sent by `machfrom' if it finds it
69 * either in the receive queue, or in a linked list of previously received
70 * messages that it maintains.
71 * Otherwise it waits to see if the appropriate message arrives within
72 * `intvl' seconds. If not, it returns NULL.
73 */
74
75struct tsp *
76readmsg(int type, char *machfrom, struct timeval *intvl,
77	struct netinfo *netfrom)
78{
79	socklen_t length;
80	struct pollfd set[1];
81	static struct tsplist *head = &msgslist;
82	static struct tsplist *tail = &msgslist;
83	static int msgcnt = 0;
84	struct tsplist *prev;
85	struct netinfo *ntp;
86	struct tsplist *ptr;
87	ssize_t n;
88
89	if (trace) {
90		fprintf(fd, "readmsg: looking for %s from %s, %s\n",
91			tsptype[type], machfrom == NULL ? "ANY" : machfrom,
92			netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net));
93		if (head->p != 0) {
94			length = 1;
95			for (ptr = head->p; ptr != 0; ptr = ptr->p) {
96				/* do not repeat the hundreds of messages */
97				if (++length > 3) {
98					if (ptr == tail) {
99						fprintf(fd,"\t ...%d skipped\n",
100							length);
101					} else {
102						continue;
103					}
104				}
105				fprintf(fd, length > 1 ? "\t" : "queue:\t");
106				print(&ptr->info, &ptr->addr);
107			}
108		}
109	}
110
111	ptr = head->p;
112	prev = head;
113
114	/*
115	 * Look for the requested message scanning through the
116	 * linked list. If found, return it and free the space
117	 */
118
119	while (ptr != NULL) {
120		if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
121again:
122			msgin = ptr->info;
123			from = ptr->addr;
124			from_when = ptr->when;
125			prev->p = ptr->p;
126			if (ptr == tail)
127				tail = prev;
128			free(ptr);
129			fromnet = NULL;
130			if (netfrom == NULL)
131			    for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
132				    if ((ntp->mask & from.sin_addr.s_addr) ==
133					ntp->net.s_addr) {
134					    fromnet = ntp;
135					    break;
136				    }
137			    }
138			else
139			    fromnet = netfrom;
140			if (trace) {
141				fprintf(fd, "readmsg: found ");
142				print(&msgin, &from);
143			}
144
145/* The protocol can get far behind.  When it does, it gets
146 *	hopelessly confused.  So delete duplicate messages.
147 */
148			for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) {
149				if (ptr->addr.sin_addr.s_addr
150					== from.sin_addr.s_addr
151				    && ptr->info.tsp_type == msgin.tsp_type) {
152					if (trace)
153						fprintf(fd, "\tdup ");
154					goto again;
155				}
156			}
157			msgcnt--;
158			return(&msgin);
159		} else {
160			prev = ptr;
161			ptr = ptr->p;
162		}
163	}
164
165	/*
166	 * If the message was not in the linked list, it may still be
167	 * coming from the network. Set the timer and wait
168	 * on a select to read the next incoming message: if it is the
169	 * right one, return it, otherwise insert it in the linked list.
170	 */
171
172	(void)gettimeofday(&rtout, 0);
173	timeradd(&rtout, intvl, &rtout);
174	set[0].fd = sock;
175	set[0].events = POLLIN;
176	for (;;) {
177		(void)gettimeofday(&rtime, 0);
178		timersub(&rtout, &rtime, &rwait);
179		if (rwait.tv_sec < 0)
180			rwait.tv_sec = rwait.tv_usec = 0;
181		else if (rwait.tv_sec == 0
182			 && rwait.tv_usec < 1000000/CLK_TCK)
183			rwait.tv_usec = 1000000/CLK_TCK;
184
185		if (trace) {
186			fprintf(fd, "readmsg: wait %ld.%6ld at %s\n",
187			    (long int)rwait.tv_sec, (long int)rwait.tv_usec,
188			    date());
189			/* Notice a full disk, as we flush trace info.
190			 * It is better to flush periodically than at
191			 * every line because the tracing consists of bursts
192			 * of many lines.  Without care, tracing slows
193			 * down the code enough to break the protocol.
194			 */
195			if (rwait.tv_sec != 0
196			    && EOF == fflush(fd))
197				traceoff("Tracing ended for cause");
198		}
199
200		if (!poll(set, 1, (int)(rwait.tv_sec * 1000 + rwait.tv_usec / 1000))) {
201			if (rwait.tv_sec == 0 && rwait.tv_usec == 0)
202				return(0);
203			continue;
204		}
205		length = sizeof(from);
206		if ((n = recvfrom(sock, &msgin, sizeof(struct tsp), 0,
207			     (struct sockaddr*)(void *)&from, &length)) < 0) {
208			syslog(LOG_ERR, "recvfrom: %m");
209			exit(EXIT_FAILURE);
210		}
211		if (n < (ssize_t)sizeof(struct tsp)) {
212			syslog(LOG_NOTICE,
213			    "short packet (%lu/%lu bytes) from %s",
214			      (u_long)n, (u_long)sizeof(struct tsp),
215			      inet_ntoa(from.sin_addr));
216			continue;
217		}
218		(void)gettimeofday(&from_when, (struct timezone *)0);
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(ptr);
321			}
322			tail = head;
323		} else {
324			tail->p = malloc(sizeof(struct tsplist));
325			tail = tail->p;
326			tail->p = NULL;
327			tail->info = msgin;
328			tail->addr = from;
329			/* timestamp msgs so SETTIMEs are correct */
330			tail->when = from_when;
331		}
332	}
333}
334
335/*
336 * Send the necessary acknowledgements:
337 * only the type ACK is to be sent by a slave
338 */
339void
340slaveack(void)
341{
342	switch(msgin.tsp_type) {
343
344	case TSP_ADJTIME:
345	case TSP_SETTIME:
346	case TSP_ACCEPT:
347	case TSP_REFUSE:
348	case TSP_TRACEON:
349	case TSP_TRACEOFF:
350	case TSP_QUIT:
351		if (trace) {
352			fprintf(fd, "Slaveack: ");
353			print(&msgin, &from);
354		}
355		xmit(TSP_ACK,msgin.tsp_seq, &from);
356		break;
357
358	default:
359		if (trace) {
360			fprintf(fd, "Slaveack: no ack: ");
361			print(&msgin, &from);
362		}
363		break;
364	}
365}
366
367/*
368 * Certain packets may arrive from this machine on ignored networks.
369 * These packets should be acknowledged.
370 */
371void
372ignoreack(void)
373{
374	switch(msgin.tsp_type) {
375
376	case TSP_TRACEON:
377	case TSP_TRACEOFF:
378	case TSP_QUIT:
379		if (trace) {
380			fprintf(fd, "Ignoreack: ");
381			print(&msgin, &from);
382		}
383		xmit(TSP_ACK,msgin.tsp_seq, &from);
384		break;
385
386	default:
387		if (trace) {
388			fprintf(fd, "Ignoreack: no ack: ");
389			print(&msgin, &from);
390		}
391		break;
392	}
393}
394
395/*
396 * `masterack' sends the necessary acknowledgements
397 * to the messages received by a master
398 */
399void
400masterack(void)
401{
402	struct tsp resp;
403
404	resp = msgin;
405	resp.tsp_vers = TSPVERSION;
406	set_tsp_name(&resp, hostname);
407
408	switch(msgin.tsp_type) {
409
410	case TSP_QUIT:
411	case TSP_TRACEON:
412	case TSP_TRACEOFF:
413	case TSP_MSITEREQ:
414		if (trace) {
415			fprintf(fd, "Masterack: ");
416			print(&msgin, &from);
417		}
418		xmit(TSP_ACK,msgin.tsp_seq, &from);
419		break;
420
421	case TSP_RESOLVE:
422	case TSP_MASTERREQ:
423		if (trace) {
424			fprintf(fd, "Masterack: ");
425			print(&msgin, &from);
426		}
427		xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
428		break;
429
430	default:
431		if (trace) {
432			fprintf(fd,"Masterack: no ack: ");
433			print(&msgin, &from);
434		}
435		break;
436	}
437}
438
439/*
440 * Print a TSP message
441 */
442void
443print(struct tsp *msg, struct sockaddr_in *addr)
444{
445	char tm[26];
446	time_t msgtime;
447
448	if (msg->tsp_type >= TSPTYPENUMBER) {
449		fprintf(fd, "bad type (%u) on packet from %s\n",
450		  msg->tsp_type, inet_ntoa(addr->sin_addr));
451		return;
452	}
453
454	switch (msg->tsp_type) {
455
456	case TSP_LOOP:
457		fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
458			tsptype[msg->tsp_type],
459			msg->tsp_vers,
460			msg->tsp_seq,
461			msg->tsp_hopcnt,
462			inet_ntoa(addr->sin_addr),
463			msg->tsp_name);
464		break;
465
466	case TSP_SETTIME:
467	case TSP_SETDATE:
468	case TSP_SETDATEREQ:
469		msgtime = msg->tsp_time.tv_sec;
470		strlcpy(tm, ctime(&msgtime)+3+1, sizeof(tm));
471		tm[15] = '\0';		/* ugh */
472		fprintf(fd, "%s %d %-6u %s %-15s %s\n",
473			tsptype[msg->tsp_type],
474			msg->tsp_vers,
475			msg->tsp_seq,
476			tm,
477			inet_ntoa(addr->sin_addr),
478			msg->tsp_name);
479		break;
480
481	case TSP_ADJTIME:
482		fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n",
483			tsptype[msg->tsp_type],
484			msg->tsp_vers,
485			msg->tsp_seq,
486			(long)msg->tsp_time.tv_sec,
487			(long)msg->tsp_time.tv_usec,
488			inet_ntoa(addr->sin_addr),
489			msg->tsp_name);
490		break;
491
492	default:
493		fprintf(fd, "%s %d %-6u %-15s %s\n",
494			tsptype[msg->tsp_type],
495			msg->tsp_vers,
496			msg->tsp_seq,
497			inet_ntoa(addr->sin_addr),
498			msg->tsp_name);
499		break;
500	}
501}
502