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