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