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