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