1 /* $NetBSD: readmsg.c,v 1.13 2002/09/19 00:01:33 mycroft 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.13 2002/09/19 00:01:33 mycroft 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 struct pollfd set[1]; 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 set[0].fd = sock; 179 set[0].events = POLLIN; 180 for (;;) { 181 (void)gettimeofday(&rtime, 0); 182 timersub(&rtout, &rtime, &rwait); 183 if (rwait.tv_sec < 0) 184 rwait.tv_sec = rwait.tv_usec = 0; 185 else if (rwait.tv_sec == 0 186 && rwait.tv_usec < 1000000/CLK_TCK) 187 rwait.tv_usec = 1000000/CLK_TCK; 188 189 if (trace) { 190 fprintf(fd, "readmsg: wait %ld.%6ld at %s\n", 191 (long int)rwait.tv_sec, (long int)rwait.tv_usec, 192 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 if (!poll(set, 1, rwait.tv_sec * 1000 + rwait.tv_usec / 1000)) { 205 if (rwait.tv_sec == 0 && rwait.tv_usec == 0) 206 return(0); 207 continue; 208 } 209 length = sizeof(from); 210 if ((n = recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0, 211 (struct sockaddr*)&from, &length)) < 0) { 212 syslog(LOG_ERR, "recvfrom: %m"); 213 exit(1); 214 } 215 if (n < (ssize_t)sizeof(struct tsp)) { 216 syslog(LOG_NOTICE, 217 "short packet (%lu/%lu bytes) from %s", 218 (u_long)n, (u_long)sizeof(struct tsp), 219 inet_ntoa(from.sin_addr)); 220 continue; 221 } 222 (void)gettimeofday(&from_when, (struct timezone *)0); 223 bytehostorder(&msgin); 224 225 if (msgin.tsp_vers > TSPVERSION) { 226 if (trace) { 227 fprintf(fd,"readmsg: version mismatch\n"); 228 /* should do a dump of the packet */ 229 } 230 continue; 231 } 232 233 if (memchr(msgin.tsp_name, 234 '\0', sizeof msgin.tsp_name) == NULL) { 235 syslog(LOG_NOTICE, "hostname field not NUL terminated " 236 "in packet from %s", inet_ntoa(from.sin_addr)); 237 continue; 238 } 239 240 fromnet = NULL; 241 for (ntp = nettab; ntp != NULL; ntp = ntp->next) 242 if ((ntp->mask & from.sin_addr.s_addr) == 243 ntp->net.s_addr) { 244 fromnet = ntp; 245 break; 246 } 247 248 /* 249 * drop packets from nets we are ignoring permanently 250 */ 251 if (fromnet == NULL) { 252 /* 253 * The following messages may originate on 254 * this host with an ignored network address 255 */ 256 if (msgin.tsp_type != TSP_TRACEON && 257 msgin.tsp_type != TSP_SETDATE && 258 msgin.tsp_type != TSP_MSITE && 259 msgin.tsp_type != TSP_TEST && 260 msgin.tsp_type != TSP_TRACEOFF) { 261 if (trace) { 262 fprintf(fd,"readmsg: discard null net "); 263 print(&msgin, &from); 264 } 265 continue; 266 } 267 } 268 269 /* 270 * Throw away messages coming from this machine, 271 * unless they are of some particular type. 272 * This gets rid of broadcast messages and reduces 273 * master processing time. 274 */ 275 if (!strcmp(msgin.tsp_name, hostname) 276 && msgin.tsp_type != TSP_SETDATE 277 && msgin.tsp_type != TSP_TEST 278 && msgin.tsp_type != TSP_MSITE 279 && msgin.tsp_type != TSP_TRACEON 280 && msgin.tsp_type != TSP_TRACEOFF 281 && msgin.tsp_type != TSP_LOOP) { 282 if (trace) { 283 fprintf(fd, "readmsg: discard own "); 284 print(&msgin, &from); 285 } 286 continue; 287 } 288 289 /* 290 * Send acknowledgements here; this is faster and 291 * avoids deadlocks that would occur if acks were 292 * sent from a higher level routine. Different 293 * acknowledgements are necessary, depending on 294 * status. 295 */ 296 if (fromnet == NULL) /* do not de-reference 0 */ 297 ignoreack(); 298 else if (fromnet->status == MASTER) 299 masterack(); 300 else if (fromnet->status == SLAVE) 301 slaveack(); 302 else 303 ignoreack(); 304 305 if (LOOKAT(msgin, type, machfrom, netfrom, from)) { 306 if (trace) { 307 fprintf(fd, "readmsg: "); 308 print(&msgin, &from); 309 } 310 return(&msgin); 311 } else if (++msgcnt > NHOSTS*3) { 312 313 /* The protocol gets hopelessly confused if it gets too far 314 * behind. However, it seems able to recover from all cases of lost 315 * packets. Therefore, if we are swamped, throw everything away. 316 */ 317 if (trace) 318 fprintf(fd, 319 "readmsg: discarding %d msgs\n", 320 msgcnt); 321 msgcnt = 0; 322 while ((ptr=head->p) != NULL) { 323 head->p = ptr->p; 324 free((char *)ptr); 325 } 326 tail = head; 327 } else { 328 tail->p = (struct tsplist *) 329 malloc(sizeof(struct tsplist)); 330 tail = tail->p; 331 tail->p = NULL; 332 tail->info = msgin; 333 tail->addr = from; 334 /* timestamp msgs so SETTIMEs are correct */ 335 tail->when = from_when; 336 } 337 } 338 } 339 340 /* 341 * Send the necessary acknowledgements: 342 * only the type ACK is to be sent by a slave 343 */ 344 void 345 slaveack(void) 346 { 347 switch(msgin.tsp_type) { 348 349 case TSP_ADJTIME: 350 case TSP_SETTIME: 351 case TSP_ACCEPT: 352 case TSP_REFUSE: 353 case TSP_TRACEON: 354 case TSP_TRACEOFF: 355 case TSP_QUIT: 356 if (trace) { 357 fprintf(fd, "Slaveack: "); 358 print(&msgin, &from); 359 } 360 xmit(TSP_ACK,msgin.tsp_seq, &from); 361 break; 362 363 default: 364 if (trace) { 365 fprintf(fd, "Slaveack: no ack: "); 366 print(&msgin, &from); 367 } 368 break; 369 } 370 } 371 372 /* 373 * Certain packets may arrive from this machine on ignored networks. 374 * These packets should be acknowledged. 375 */ 376 void 377 ignoreack(void) 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 acknowledgements 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 (void)strcpy(resp.tsp_name, hostname); 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 msgtime; 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 msgtime = msg->tsp_time.tv_sec; 475 strncpy(tm, ctime(&msgtime)+3+1, sizeof(tm)); 476 tm[15] = '\0'; /* ugh */ 477 fprintf(fd, "%s %d %-6u %s %-15s %s\n", 478 tsptype[msg->tsp_type], 479 msg->tsp_vers, 480 msg->tsp_seq, 481 tm, 482 inet_ntoa(addr->sin_addr), 483 msg->tsp_name); 484 break; 485 486 case TSP_ADJTIME: 487 fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n", 488 tsptype[msg->tsp_type], 489 msg->tsp_vers, 490 msg->tsp_seq, 491 (long)msg->tsp_time.tv_sec, 492 (long)msg->tsp_time.tv_usec, 493 inet_ntoa(addr->sin_addr), 494 msg->tsp_name); 495 break; 496 497 default: 498 fprintf(fd, "%s %d %-6u %-15s %s\n", 499 tsptype[msg->tsp_type], 500 msg->tsp_vers, 501 msg->tsp_seq, 502 inet_ntoa(addr->sin_addr), 503 msg->tsp_name); 504 break; 505 } 506 } 507