/* $NetBSD: readmsg.c,v 1.23 2017/08/11 16:47:42 ginsbach Exp $ */ /*- * Copyright (c) 1985, 1993 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint #if 0 static char sccsid[] = "@(#)readmsg.c 8.1 (Berkeley) 6/6/93"; #else __RCSID("$NetBSD: readmsg.c,v 1.23 2017/08/11 16:47:42 ginsbach Exp $"); #endif #endif /* not lint */ #include "globals.h" extern const char * const tsptype[]; /* * LOOKAT checks if the message is of the requested type and comes from * the right machine, returning 1 in case of affirmative answer */ #define LOOKAT(msg, mtype, mfrom, netp, froms) \ (((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) && \ ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) && \ ((netp) == 0 || \ ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr)) struct timeval rtime, rwait, rtout; struct tsp msgin; static struct tsplist { struct tsp info; struct timeval when; struct sockaddr_in addr; struct tsplist *p; } msgslist; struct sockaddr_in from; struct netinfo *fromnet; struct timeval from_when; /* * `readmsg' returns message `type' sent by `machfrom' if it finds it * either in the receive queue, or in a linked list of previously received * messages that it maintains. * Otherwise it waits to see if the appropriate message arrives within * `intvl' seconds. If not, it returns NULL. */ struct tsp * readmsg(int type, char *machfrom, struct timeval *intvl, struct netinfo *netfrom) { socklen_t length; struct pollfd set[1]; static struct tsplist *head = &msgslist; static struct tsplist *tail = &msgslist; static int msgcnt = 0; struct tsplist *prev; struct netinfo *ntp; struct tsplist *ptr; ssize_t n; if (trace) { fprintf(fd, "readmsg: looking for %s from %s, %s\n", tsptype[type], machfrom == NULL ? "ANY" : machfrom, netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net)); if (head->p != 0) { length = 1; for (ptr = head->p; ptr != 0; ptr = ptr->p) { /* do not repeat the hundreds of messages */ if (++length > 3) { if (ptr == tail) { fprintf(fd,"\t ...%d skipped\n", length); } else { continue; } } fprintf(fd, length > 1 ? "\t" : "queue:\t"); print(&ptr->info, &ptr->addr); } } } ptr = head->p; prev = head; /* * Look for the requested message scanning through the * linked list. If found, return it and free the space */ while (ptr != NULL) { if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) { again: msgin = ptr->info; from = ptr->addr; from_when = ptr->when; prev->p = ptr->p; if (ptr == tail) tail = prev; free(ptr); fromnet = NULL; if (netfrom == NULL) for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if ((ntp->mask & from.sin_addr.s_addr) == ntp->net.s_addr) { fromnet = ntp; break; } } else fromnet = netfrom; if (trace) { fprintf(fd, "readmsg: found "); print(&msgin, &from); } /* The protocol can get far behind. When it does, it gets * hopelessly confused. So delete duplicate messages. */ for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) { if (ptr->addr.sin_addr.s_addr == from.sin_addr.s_addr && ptr->info.tsp_type == msgin.tsp_type) { if (trace) fprintf(fd, "\tdup "); goto again; } } msgcnt--; return(&msgin); } else { prev = ptr; ptr = ptr->p; } } /* * If the message was not in the linked list, it may still be * coming from the network. Set the timer and wait * on a select to read the next incoming message: if it is the * right one, return it, otherwise insert it in the linked list. */ (void)gettimeofday(&rtout, 0); timeradd(&rtout, intvl, &rtout); set[0].fd = sock; set[0].events = POLLIN; for (;;) { (void)gettimeofday(&rtime, 0); timersub(&rtout, &rtime, &rwait); if (rwait.tv_sec < 0) rwait.tv_sec = rwait.tv_usec = 0; else if (rwait.tv_sec == 0 && rwait.tv_usec < 1000000/CLK_TCK) rwait.tv_usec = 1000000/CLK_TCK; if (trace) { fprintf(fd, "readmsg: wait %ld.%6ld at %s\n", (long int)rwait.tv_sec, (long int)rwait.tv_usec, date()); /* Notice a full disk, as we flush trace info. * It is better to flush periodically than at * every line because the tracing consists of bursts * of many lines. Without care, tracing slows * down the code enough to break the protocol. */ if (rwait.tv_sec != 0 && EOF == fflush(fd)) traceoff("Tracing ended for cause"); } if (!poll(set, 1, (int)(rwait.tv_sec * 1000 + rwait.tv_usec / 1000))) { if (rwait.tv_sec == 0 && rwait.tv_usec == 0) return(0); continue; } length = sizeof(from); memset(&msgin, 0, sizeof(msgin)); if ((n = recvfrom(sock, &msgin, sizeof(struct tsp), 0, (struct sockaddr*)(void *)&from, &length)) < 0) { syslog(LOG_ERR, "recvfrom: %m"); exit(EXIT_FAILURE); } /* * The 4.3BSD protocol spec had a 32-byte tsp_name field, and * this is still OS-dependent. Demand that the packet is at * least long enough to hold a 4.3BSD packet. */ if (n < (ssize_t)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) { syslog(LOG_NOTICE, "short packet (%lu/%lu bytes) from %s", (u_long)n, (u_long)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32), inet_ntoa(from.sin_addr)); continue; } (void)gettimeofday(&from_when, (struct timezone *)0); bytehostorder(&msgin); if (msgin.tsp_vers > TSPVERSION) { if (trace) { fprintf(fd,"readmsg: version mismatch\n"); /* should do a dump of the packet */ } continue; } if (memchr(msgin.tsp_name, '\0', sizeof msgin.tsp_name) == NULL) { syslog(LOG_NOTICE, "hostname field not NUL terminated " "in packet from %s", inet_ntoa(from.sin_addr)); continue; } fromnet = NULL; for (ntp = nettab; ntp != NULL; ntp = ntp->next) if ((ntp->mask & from.sin_addr.s_addr) == ntp->net.s_addr) { fromnet = ntp; break; } /* * drop packets from nets we are ignoring permanently */ if (fromnet == NULL) { /* * The following messages may originate on * this host with an ignored network address */ if (msgin.tsp_type != TSP_TRACEON && msgin.tsp_type != TSP_SETDATE && msgin.tsp_type != TSP_MSITE && msgin.tsp_type != TSP_TEST && msgin.tsp_type != TSP_TRACEOFF) { if (trace) { fprintf(fd,"readmsg: discard null net "); print(&msgin, &from); } continue; } } /* * Throw away messages coming from this machine, * unless they are of some particular type. * This gets rid of broadcast messages and reduces * master processing time. */ if (!strcmp(msgin.tsp_name, hostname) && msgin.tsp_type != TSP_SETDATE && msgin.tsp_type != TSP_TEST && msgin.tsp_type != TSP_MSITE && msgin.tsp_type != TSP_TRACEON && msgin.tsp_type != TSP_TRACEOFF && msgin.tsp_type != TSP_LOOP) { if (trace) { fprintf(fd, "readmsg: discard own "); print(&msgin, &from); } continue; } /* * Send acknowledgements here; this is faster and * avoids deadlocks that would occur if acks were * sent from a higher level routine. Different * acknowledgements are necessary, depending on * status. */ if (fromnet == NULL) /* do not de-reference 0 */ ignoreack(); else if (fromnet->status == MASTER) masterack(); else if (fromnet->status == SLAVE) slaveack(); else ignoreack(); if (LOOKAT(msgin, type, machfrom, netfrom, from)) { if (trace) { fprintf(fd, "readmsg: "); print(&msgin, &from); } return(&msgin); } else if (++msgcnt > NHOSTS*3) { /* The protocol gets hopelessly confused if it gets too far * behind. However, it seems able to recover from all cases of lost * packets. Therefore, if we are swamped, throw everything away. */ if (trace) fprintf(fd, "readmsg: discarding %d msgs\n", msgcnt); msgcnt = 0; while ((ptr = head->p) != NULL) { head->p = ptr->p; free(ptr); } tail = head; } else { tail->p = malloc(sizeof(struct tsplist)); tail = tail->p; tail->p = NULL; tail->info = msgin; tail->addr = from; /* timestamp msgs so SETTIMEs are correct */ tail->when = from_when; } } } /* * Send the necessary acknowledgements: * only the type ACK is to be sent by a slave */ void slaveack(void) { switch(msgin.tsp_type) { case TSP_ADJTIME: case TSP_SETTIME: case TSP_ACCEPT: case TSP_REFUSE: case TSP_TRACEON: case TSP_TRACEOFF: case TSP_QUIT: if (trace) { fprintf(fd, "Slaveack: "); print(&msgin, &from); } xmit(TSP_ACK,msgin.tsp_seq, &from); break; default: if (trace) { fprintf(fd, "Slaveack: no ack: "); print(&msgin, &from); } break; } } /* * Certain packets may arrive from this machine on ignored networks. * These packets should be acknowledged. */ void ignoreack(void) { switch(msgin.tsp_type) { case TSP_TRACEON: case TSP_TRACEOFF: case TSP_QUIT: if (trace) { fprintf(fd, "Ignoreack: "); print(&msgin, &from); } xmit(TSP_ACK,msgin.tsp_seq, &from); break; default: if (trace) { fprintf(fd, "Ignoreack: no ack: "); print(&msgin, &from); } break; } } /* * `masterack' sends the necessary acknowledgements * to the messages received by a master */ void masterack(void) { struct tsp resp; resp = msgin; resp.tsp_vers = TSPVERSION; set_tsp_name(&resp, hostname); switch(msgin.tsp_type) { case TSP_QUIT: case TSP_TRACEON: case TSP_TRACEOFF: case TSP_MSITEREQ: if (trace) { fprintf(fd, "Masterack: "); print(&msgin, &from); } xmit(TSP_ACK,msgin.tsp_seq, &from); break; case TSP_RESOLVE: case TSP_MASTERREQ: if (trace) { fprintf(fd, "Masterack: "); print(&msgin, &from); } xmit(TSP_MASTERACK,msgin.tsp_seq, &from); break; default: if (trace) { fprintf(fd,"Masterack: no ack: "); print(&msgin, &from); } break; } } /* * Print a TSP message */ void print(struct tsp *msg, struct sockaddr_in *addr) { char tm[26]; time_t msgtime; if (msg->tsp_type >= TSPTYPENUMBER) { fprintf(fd, "bad type (%u) on packet from %s\n", msg->tsp_type, inet_ntoa(addr->sin_addr)); return; } switch (msg->tsp_type) { case TSP_LOOP: fprintf(fd, "%s %d %-6u #%d %-15s %s\n", tsptype[msg->tsp_type], msg->tsp_vers, msg->tsp_seq, msg->tsp_hopcnt, inet_ntoa(addr->sin_addr), msg->tsp_name); break; case TSP_SETTIME: case TSP_SETDATE: case TSP_SETDATEREQ: msgtime = msg->tsp_time.tv_sec; strlcpy(tm, ctime(&msgtime)+3+1, sizeof(tm)); tm[15] = '\0'; /* ugh */ fprintf(fd, "%s %d %-6u %s %-15s %s\n", tsptype[msg->tsp_type], msg->tsp_vers, msg->tsp_seq, tm, inet_ntoa(addr->sin_addr), msg->tsp_name); break; case TSP_ADJTIME: fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n", tsptype[msg->tsp_type], msg->tsp_vers, msg->tsp_seq, (long)msg->tsp_time.tv_sec, (long)msg->tsp_time.tv_usec, inet_ntoa(addr->sin_addr), msg->tsp_name); break; default: fprintf(fd, "%s %d %-6u %-15s %s\n", tsptype[msg->tsp_type], msg->tsp_vers, msg->tsp_seq, inet_ntoa(addr->sin_addr), msg->tsp_name); break; } }