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