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