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