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