xref: /netbsd-src/usr.sbin/timed/timed/readmsg.c (revision 94951b0fc5ac44d7dd8f2e100ddc23f19b3dead7)
1 /*	$NetBSD: readmsg.c,v 1.23 2017/08/11 16:47:42 ginsbach 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
35 static char sccsid[] = "@(#)readmsg.c	8.1 (Berkeley) 6/6/93";
36 #else
37 __RCSID("$NetBSD: readmsg.c,v 1.23 2017/08/11 16:47:42 ginsbach Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include "globals.h"
42 
43 extern 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 
55 struct timeval rtime, rwait, rtout;
56 struct tsp msgin;
57 static struct tsplist {
58 	struct tsp info;
59 	struct timeval when;
60 	struct sockaddr_in addr;
61 	struct tsplist *p;
62 } msgslist;
63 struct sockaddr_in from;
64 struct netinfo *fromnet;
65 struct 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 
75 struct tsp *
readmsg(int type,char * machfrom,struct timeval * intvl,struct netinfo * netfrom)76 readmsg(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)) {
121 again:
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 		memset(&msgin, 0, sizeof(msgin));
207 		if ((n = recvfrom(sock, &msgin, sizeof(struct tsp), 0,
208 			     (struct sockaddr*)(void *)&from, &length)) < 0) {
209 			syslog(LOG_ERR, "recvfrom: %m");
210 			exit(EXIT_FAILURE);
211 		}
212 		/*
213 		 * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
214 		 * this is still OS-dependent.  Demand that the packet is at
215 		 * least long enough to hold a 4.3BSD packet.
216 		 */
217 		if (n < (ssize_t)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
218 			syslog(LOG_NOTICE,
219 			    "short packet (%lu/%lu bytes) from %s",
220 			      (u_long)n,
221 			      (u_long)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32),
222 			      inet_ntoa(from.sin_addr));
223 			continue;
224 		}
225 		(void)gettimeofday(&from_when, (struct timezone *)0);
226 		bytehostorder(&msgin);
227 
228 		if (msgin.tsp_vers > TSPVERSION) {
229 			if (trace) {
230 			    fprintf(fd,"readmsg: version mismatch\n");
231 			    /* should do a dump of the packet */
232 			}
233 			continue;
234 		}
235 
236 		if (memchr(msgin.tsp_name,
237 		    '\0', sizeof msgin.tsp_name) == NULL) {
238 			syslog(LOG_NOTICE, "hostname field not NUL terminated "
239 			    "in packet from %s", inet_ntoa(from.sin_addr));
240 			continue;
241 		}
242 
243 		fromnet = NULL;
244 		for (ntp = nettab; ntp != NULL; ntp = ntp->next)
245 			if ((ntp->mask & from.sin_addr.s_addr) ==
246 			    ntp->net.s_addr) {
247 				fromnet = ntp;
248 				break;
249 			}
250 
251 		/*
252 		 * drop packets from nets we are ignoring permanently
253 		 */
254 		if (fromnet == NULL) {
255 			/*
256 			 * The following messages may originate on
257 			 * this host with an ignored network address
258 			 */
259 			if (msgin.tsp_type != TSP_TRACEON &&
260 			    msgin.tsp_type != TSP_SETDATE &&
261 			    msgin.tsp_type != TSP_MSITE &&
262 			    msgin.tsp_type != TSP_TEST &&
263 			    msgin.tsp_type != TSP_TRACEOFF) {
264 				if (trace) {
265 				    fprintf(fd,"readmsg: discard null net ");
266 				    print(&msgin, &from);
267 				}
268 				continue;
269 			}
270 		}
271 
272 		/*
273 		 * Throw away messages coming from this machine,
274 		 * unless they are of some particular type.
275 		 * This gets rid of broadcast messages and reduces
276 		 * master processing time.
277 		 */
278 		if (!strcmp(msgin.tsp_name, hostname)
279 		    && msgin.tsp_type != TSP_SETDATE
280 		    && msgin.tsp_type != TSP_TEST
281 		    && msgin.tsp_type != TSP_MSITE
282 		    && msgin.tsp_type != TSP_TRACEON
283 		    && msgin.tsp_type != TSP_TRACEOFF
284 		    && msgin.tsp_type != TSP_LOOP) {
285 			if (trace) {
286 				fprintf(fd, "readmsg: discard own ");
287 				print(&msgin, &from);
288 			}
289 			continue;
290 		}
291 
292 		/*
293 		 * Send acknowledgements here; this is faster and
294 		 * avoids deadlocks that would occur if acks were
295 		 * sent from a higher level routine.  Different
296 		 * acknowledgements are necessary, depending on
297 		 * status.
298 		 */
299 		if (fromnet == NULL)	/* do not de-reference 0 */
300 			ignoreack();
301 		else if (fromnet->status == MASTER)
302 			masterack();
303 		else if (fromnet->status == SLAVE)
304 			slaveack();
305 		else
306 			ignoreack();
307 
308 		if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
309 			if (trace) {
310 				fprintf(fd, "readmsg: ");
311 				print(&msgin, &from);
312 			}
313 			return(&msgin);
314 		} else if (++msgcnt > NHOSTS*3) {
315 
316 /* The protocol gets hopelessly confused if it gets too far
317 *	behind.  However, it seems able to recover from all cases of lost
318 *	packets.  Therefore, if we are swamped, throw everything away.
319 */
320 			if (trace)
321 				fprintf(fd,
322 					"readmsg: discarding %d msgs\n",
323 					msgcnt);
324 			msgcnt = 0;
325 			while ((ptr = head->p) != NULL) {
326 				head->p = ptr->p;
327 				free(ptr);
328 			}
329 			tail = head;
330 		} else {
331 			tail->p = malloc(sizeof(struct tsplist));
332 			tail = tail->p;
333 			tail->p = NULL;
334 			tail->info = msgin;
335 			tail->addr = from;
336 			/* timestamp msgs so SETTIMEs are correct */
337 			tail->when = from_when;
338 		}
339 	}
340 }
341 
342 /*
343  * Send the necessary acknowledgements:
344  * only the type ACK is to be sent by a slave
345  */
346 void
slaveack(void)347 slaveack(void)
348 {
349 	switch(msgin.tsp_type) {
350 
351 	case TSP_ADJTIME:
352 	case TSP_SETTIME:
353 	case TSP_ACCEPT:
354 	case TSP_REFUSE:
355 	case TSP_TRACEON:
356 	case TSP_TRACEOFF:
357 	case TSP_QUIT:
358 		if (trace) {
359 			fprintf(fd, "Slaveack: ");
360 			print(&msgin, &from);
361 		}
362 		xmit(TSP_ACK,msgin.tsp_seq, &from);
363 		break;
364 
365 	default:
366 		if (trace) {
367 			fprintf(fd, "Slaveack: no ack: ");
368 			print(&msgin, &from);
369 		}
370 		break;
371 	}
372 }
373 
374 /*
375  * Certain packets may arrive from this machine on ignored networks.
376  * These packets should be acknowledged.
377  */
378 void
ignoreack(void)379 ignoreack(void)
380 {
381 	switch(msgin.tsp_type) {
382 
383 	case TSP_TRACEON:
384 	case TSP_TRACEOFF:
385 	case TSP_QUIT:
386 		if (trace) {
387 			fprintf(fd, "Ignoreack: ");
388 			print(&msgin, &from);
389 		}
390 		xmit(TSP_ACK,msgin.tsp_seq, &from);
391 		break;
392 
393 	default:
394 		if (trace) {
395 			fprintf(fd, "Ignoreack: no ack: ");
396 			print(&msgin, &from);
397 		}
398 		break;
399 	}
400 }
401 
402 /*
403  * `masterack' sends the necessary acknowledgements
404  * to the messages received by a master
405  */
406 void
masterack(void)407 masterack(void)
408 {
409 	struct tsp resp;
410 
411 	resp = msgin;
412 	resp.tsp_vers = TSPVERSION;
413 	set_tsp_name(&resp, hostname);
414 
415 	switch(msgin.tsp_type) {
416 
417 	case TSP_QUIT:
418 	case TSP_TRACEON:
419 	case TSP_TRACEOFF:
420 	case TSP_MSITEREQ:
421 		if (trace) {
422 			fprintf(fd, "Masterack: ");
423 			print(&msgin, &from);
424 		}
425 		xmit(TSP_ACK,msgin.tsp_seq, &from);
426 		break;
427 
428 	case TSP_RESOLVE:
429 	case TSP_MASTERREQ:
430 		if (trace) {
431 			fprintf(fd, "Masterack: ");
432 			print(&msgin, &from);
433 		}
434 		xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
435 		break;
436 
437 	default:
438 		if (trace) {
439 			fprintf(fd,"Masterack: no ack: ");
440 			print(&msgin, &from);
441 		}
442 		break;
443 	}
444 }
445 
446 /*
447  * Print a TSP message
448  */
449 void
print(struct tsp * msg,struct sockaddr_in * addr)450 print(struct tsp *msg, struct sockaddr_in *addr)
451 {
452 	char tm[26];
453 	time_t msgtime;
454 
455 	if (msg->tsp_type >= TSPTYPENUMBER) {
456 		fprintf(fd, "bad type (%u) on packet from %s\n",
457 		  msg->tsp_type, inet_ntoa(addr->sin_addr));
458 		return;
459 	}
460 
461 	switch (msg->tsp_type) {
462 
463 	case TSP_LOOP:
464 		fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
465 			tsptype[msg->tsp_type],
466 			msg->tsp_vers,
467 			msg->tsp_seq,
468 			msg->tsp_hopcnt,
469 			inet_ntoa(addr->sin_addr),
470 			msg->tsp_name);
471 		break;
472 
473 	case TSP_SETTIME:
474 	case TSP_SETDATE:
475 	case TSP_SETDATEREQ:
476 		msgtime = msg->tsp_time.tv_sec;
477 		strlcpy(tm, ctime(&msgtime)+3+1, sizeof(tm));
478 		tm[15] = '\0';		/* ugh */
479 		fprintf(fd, "%s %d %-6u %s %-15s %s\n",
480 			tsptype[msg->tsp_type],
481 			msg->tsp_vers,
482 			msg->tsp_seq,
483 			tm,
484 			inet_ntoa(addr->sin_addr),
485 			msg->tsp_name);
486 		break;
487 
488 	case TSP_ADJTIME:
489 		fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n",
490 			tsptype[msg->tsp_type],
491 			msg->tsp_vers,
492 			msg->tsp_seq,
493 			(long)msg->tsp_time.tv_sec,
494 			(long)msg->tsp_time.tv_usec,
495 			inet_ntoa(addr->sin_addr),
496 			msg->tsp_name);
497 		break;
498 
499 	default:
500 		fprintf(fd, "%s %d %-6u %-15s %s\n",
501 			tsptype[msg->tsp_type],
502 			msg->tsp_vers,
503 			msg->tsp_seq,
504 			inet_ntoa(addr->sin_addr),
505 			msg->tsp_name);
506 		break;
507 	}
508 }
509