5 kx /*
5 kx NO WARRANTY
5 kx
5 kx THERE IS NO WARRANTY FOR THIS PROGRAM, TO THE EXTENT PERMITTED BY
5 kx APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
5 kx HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
5 kx OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
5 kx THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5 kx PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
5 kx IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
5 kx ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
5 kx
5 kx IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
5 kx ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
5 kx REDISTRIBUTE THE PROGRAM, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
5 kx GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
5 kx USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
5 kx DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
5 kx PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
5 kx EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
5 kx SUCH DAMAGES.
5 kx
5 kx */
5 kx
5 kx #ifndef lint
5 kx char sccsid[]="@(#) netdate.c 1.16 85/08/21";
5 kx #endif
5 kx #include <sys/param.h>
5 kx #include <sys/stat.h>
5 kx #include <sys/ioctl.h>
5 kx #include <sys/socket.h>
5 kx #include <time.h>
5 kx
5 kx #include <netinet/in.h>
5 kx
5 kx #include <fcntl.h>
5 kx #include <string.h>
5 kx #include <stdlib.h>
5 kx #include <unistd.h>
5 kx #include <stdio.h>
5 kx #include <netdb.h>
5 kx #include <setjmp.h>
5 kx #include <signal.h>
5 kx #include <utmp.h>
5 kx #define WTMP "/var/log/wtmp"
5 kx
5 kx #ifndef __GLIBC__
5 kx struct utmp wtmp[2] = {
5 kx { 0, 0, "|", "", 0, "", "", 0},
5 kx { 0, 0, "{", "", 0, "", "", 0}
5 kx };
5 kx #else
5 kx struct utmp wtmp[2] = {
5 kx { 0, 0, "|", "", "", "", {0, 0}, 0, {0, 0}, {0, 0, 0, 0}, "" },
5 kx { 0, 0, "|", "", "", "", {0, 0}, 0, {0, 0}, {0, 0, 0, 0}, "" },
5 kx };
5 kx #endif
5 kx
5 kx char *service = "time";
5 kx char *defaultproto = "udp";
5 kx /* difference between 1900 (RFC868) and 1970 (UNIX) base times */
5 kx #define NETBASE 2208988800u
5 kx
5 kx long limit = 5;
5 kx #define MAXHOSTS 20
5 kx
5 kx #define LOCALHOST "localhost"
5 kx char *whoami;
5 kx char hostname[65];
5 kx struct timeval now;
5 kx struct timehost {
5 kx char *hostname;
5 kx short local;
5 kx short bad;
5 kx char *protoname;
5 kx long protonumber;
5 kx int socktype;
5 kx struct timeval asked;
5 kx struct timeval then;
5 kx struct timeval acked;
5 kx long difference;
5 kx long count;
5 kx } timehosts[MAXHOSTS];
5 kx struct timehost *tophost = &timehosts[MAXHOSTS];
5 kx
5 kx void usage (void);
5 kx int setproto (char *, struct timehost *);
5 kx int getdiff (struct timehost *);
5 kx int getdate (struct timehost *);
5 kx void printit (struct timehost *);
5 kx void tvsub (struct timeval *, struct timeval *, struct timeval *);
5 kx void printdiff (char *, struct timeval *);
5 kx void timeout (int);
5 kx long getport (char *protoname);
5 kx
5 kx static int internettime (struct timehost *thishost);
5 kx struct timehost *mungediffs(struct timehost *);
5 kx
5 kx
5 kx void
5 kx usage (void)
5 kx {
5 kx fprintf (stderr,
5 kx "usage: %s [ -l limit ] host ...\n"
5 kx "%s tries to find a group of at least two hosts whose times agree\n"
5 kx "within %ld seconds, and sets the time to that of the first host in the group.\n",
5 kx whoami, whoami, limit);
5 kx fprintf (stderr,
5 kx "The limit may be set with the -l option. Setting it to zero (or supplying\n"
5 kx "only one host name argument) will set the time to that of the first host to\n"
5 kx "respond. The caller must be super-user for the system time to be set.\n");
5 kx
5 kx exit (1);
5 kx }
5 kx
5 kx int rdate = 0;
5 kx int verbose = 0;
5 kx int debug = 0;
5 kx
5 kx int
5 kx main (int argc, char **argv)
5 kx {
5 kx register struct timehost *thishost;
5 kx int hostargs = 0;
5 kx
5 kx /* skip warning: unused argc */
5 kx argc = argc;
5 kx
5 kx if ((whoami = rindex(*argv, '/')) != NULL)
5 kx whoami++;
5 kx else
5 kx whoami = *argv;
5 kx if (strcmp (whoami, "rdate") == 0) { /* emulate SMI rdate command */
5 kx rdate = 1;
5 kx defaultproto = "tcp";
5 kx limit = 0;
5 kx }
5 kx if (gethostname(hostname, (int)sizeof (hostname)) == -1) {
5 kx perror ("gethostname");
5 kx exit (1);
5 kx }
5 kx while (*++argv != NULL && **argv == '-') {
5 kx switch (argv[0][1]) {
5 kx case 'd':
5 kx debug++;
5 kx break;
5 kx case 'v':
5 kx verbose++;
5 kx break;
5 kx case 'l':
5 kx if (*++argv == NULL)
5 kx usage();
5 kx limit = atoi(*argv);
5 kx break;
5 kx default:
5 kx fprintf (stderr, "Unknown option: %s\n", *argv);
5 kx usage();
5 kx break;
5 kx }
5 kx }
5 kx if (*argv == NULL)
5 kx usage();
5 kx if (debug)
5 kx fprintf (stderr, "%s: rdate %d; verbose %d; limit %ld.\n",
5 kx whoami, rdate, verbose, limit);
5 kx for (thishost = &timehosts[0]; *argv != NULL; argv++) {
5 kx if (thishost >= tophost) {
5 kx fprintf(stderr, "Too many hosts: ignoring");
5 kx do {
5 kx fprintf (stderr, " %s", *argv);
5 kx } while (*++argv != NULL);
5 kx fprintf (stderr, "\n");
5 kx break;
5 kx }
5 kx if (setproto(*argv, thishost))
5 kx continue;
5 kx thishost -> hostname = *argv;
5 kx thishost -> bad = 0;
5 kx if (strcmp (thishost -> hostname, LOCALHOST) == 0)
5 kx thishost -> local = 1;
5 kx if (++hostargs == 1 && argv[1] == NULL) /* Only one host arg, */
5 kx limit = 0; /* so just set to it. */
5 kx if (limit == 0) {
5 kx if (!getdate(thishost))
5 kx continue;
5 kx exit(0);
5 kx }
5 kx if (!getdiff (thishost))
5 kx continue;
5 kx thishost++;
5 kx }
5 kx if (limit == 0)
5 kx exit(1);
5 kx if (thishost == &timehosts[0])
5 kx exit(1);
5 kx if ((thishost = mungediffs(thishost)) == NULL) {
5 kx fprintf (stderr,
5 kx "No two hosts agree on the time within %ld seconds\n",
5 kx limit);
5 kx exit(1);
5 kx }
5 kx if (!getdate (thishost))
5 kx exit (1);
5 kx exit(0);
5 kx }
5 kx
5 kx int
5 kx setproto(char *what, struct timehost *thishost)
5 kx {
5 kx static char *protoname;
5 kx static long protonumber;
5 kx static int socktype;
5 kx register struct protoent *pp;
5 kx
5 kx setprotoent(1);
5 kx if ((pp = getprotobyname (what)) == NULL) {
5 kx if (protoname == NULL)
5 kx if (!setproto(defaultproto, thishost)) {
5 kx fprintf(stderr,
5 kx "Default protocol %s was not found in /etc/protocols.\n",
5 kx defaultproto);
5 kx exit(1);
5 kx }
5 kx thishost -> protoname = protoname;
5 kx thishost -> protonumber = protonumber;
5 kx thishost -> socktype = socktype;
5 kx return(0);
5 kx }
5 kx protoname = what; /*pp -> p_name; this is static: don't use it.*/
5 kx protonumber = pp -> p_proto;
5 kx switch (protonumber) {
5 kx case IPPROTO_TCP:
5 kx socktype = SOCK_STREAM;
5 kx if (debug)
5 kx fprintf(stderr, "%s SOCK_STREAM\n", protoname);
5 kx break;
5 kx case IPPROTO_UDP:
5 kx socktype = SOCK_DGRAM;
5 kx if (debug)
5 kx fprintf(stderr, "%s SOCK_DGRAM\n", protoname);
5 kx break;
5 kx default:
5 kx fprintf(stderr, "Unknown protocol: %s\n", protoname);
5 kx exit(1);
5 kx break;
5 kx }
5 kx return(1);
5 kx }
5 kx
5 kx int
5 kx getdiff(struct timehost *thishost)
5 kx {
5 kx if (!internettime (thishost))
5 kx return(0);
5 kx thishost -> difference = thishost -> then.tv_sec - now.tv_sec;
5 kx if (!rdate)
5 kx printit(thishost);
5 kx return(1);
5 kx }
5 kx
5 kx
5 kx /*
5 kx Find the largest group of hosts which agree within the limit
5 kx and return the first of that group. If no two hosts agree,
5 kx give up.
5 kx */
5 kx
5 kx struct timehost *
5 kx mungediffs(struct timehost *tophost)
5 kx {
5 kx register struct timehost *thishost, *ahost, *goodhost;
5 kx long diff;
5 kx
5 kx tophost--; /* simplifies the comparisons */
5 kx goodhost = &timehosts[0];
5 kx for (thishost = &timehosts[0]; thishost < tophost; thishost++) {
5 kx if (thishost -> bad)
5 kx continue;
5 kx thishost -> count = 1;
5 kx if (verbose)
5 kx printf ("%s", thishost -> hostname);
5 kx for (ahost = thishost + 1; ahost <= tophost; ahost++) {
5 kx if (thishost -> bad)
5 kx continue;
5 kx diff = ahost -> difference - thishost -> difference;
5 kx if (abs(diff) < limit) {
5 kx thishost -> count++;
5 kx if (verbose)
5 kx printf (" %s", ahost -> hostname);
5 kx }
5 kx }
5 kx if (verbose) {
5 kx printf (" %ld\n", thishost -> count);
5 kx (void)fflush(stdout);
5 kx }
5 kx if (thishost -> count > goodhost -> count)
5 kx goodhost = thishost;
5 kx }
5 kx if (goodhost -> count > 1)
5 kx return(goodhost);
5 kx return(NULL);
5 kx }
5 kx
5 kx int
5 kx getdate (struct timehost *thishost)
5 kx {
5 kx int set = 0;
5 kx
5 kx if (!internettime (thishost))
5 kx return (0);
5 kx if (thishost -> local) {
5 kx printf ("Local host %s has best time, so not setting date\n",
5 kx hostname);
5 kx printit(thishost);
5 kx exit(0);
5 kx }
5 kx if (limit != 0
5 kx && abs((thishost -> then.tv_sec - now.tv_sec) - thishost -> difference)
5 kx > limit) {
5 kx fprintf (stderr,
5 kx "Time from %s has varied more than the limit of %ld seconds\n",
5 kx thishost -> hostname, limit);
5 kx printit(thishost);
5 kx exit(1);
5 kx }
5 kx if (settimeofday (&thishost -> then, (struct timezone *)0) == -1)
5 kx perror ("netdate: settimeofday");
5 kx else {
5 kx int wf;
5 kx if ((wf = open(WTMP, 1)) >= 0) {
5 kx wtmp[0].ut_time = now.tv_sec;
5 kx wtmp[1].ut_time = thishost -> then.tv_sec;
5 kx (void)lseek(wf, 0L, 2);
5 kx (void)write(wf, (char *)wtmp, sizeof(wtmp));
5 kx (void)close(wf);
5 kx }
5 kx set = 1;
5 kx }
5 kx printit(thishost);
5 kx return(set);
5 kx }
5 kx
5 kx void
5 kx printit(struct timehost *thishost)
5 kx {
5 kx struct tm *tp;
5 kx struct timeval diff;
5 kx char newstring[128];
5 kx
5 kx if (rdate)
5 kx printf ("%s", ctime((const time_t *)&thishost -> then.tv_sec));
5 kx else {
5 kx (void)sprintf(newstring, "%s ", thishost -> hostname);
5 kx tvsub(&diff, &thishost -> then, &now);
5 kx printdiff(&newstring[strlen(newstring)], &diff);
5 kx printf ("%-24s %.19s.%03ld", newstring,
5 kx ctime((const time_t *)&thishost -> then.tv_sec),
5 kx thishost -> then.tv_usec / 1000);
5 kx if (verbose) {
5 kx tp = localtime((const time_t *)&thishost -> acked);
5 kx printf(" at %02d:%02d:%02d.%03ld",
5 kx tp -> tm_hour, tp -> tm_min, tp -> tm_sec,
5 kx thishost -> acked.tv_usec / 1000);
5 kx tvsub(&diff, &thishost -> acked, &thishost -> asked);
5 kx printdiff(newstring, &diff);
5 kx printf(" delay %s", newstring);
5 kx }
5 kx printf("\n");
5 kx }
5 kx (void)fflush (stdout);
5 kx }
5 kx
5 kx void
5 kx tvsub(tdiff, t1, t0)
5 kx struct timeval *tdiff, *t1, *t0;
5 kx {
5 kx tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
5 kx tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
5 kx if (tdiff->tv_sec < 0 && tdiff->tv_usec > 0)
5 kx tdiff->tv_sec++, tdiff->tv_usec -= 1000000;
5 kx if (tdiff->tv_sec > 0 && tdiff->tv_usec < 0)
5 kx tdiff->tv_sec--, tdiff->tv_usec += 1000000;
5 kx }
5 kx
5 kx void
5 kx printdiff(char *where, struct timeval *diff)
5 kx {
5 kx (void) sprintf (where, "%c%d.%.03d",
5 kx (diff->tv_sec < 0 || diff->tv_usec < 0) ? '-' : '+',
5 kx abs(diff->tv_sec), abs(diff->tv_usec) / 1000);
5 kx }
5 kx
5 kx static jmp_buf jb;
5 kx void
5 kx timeout( int arg )
5 kx {
5 kx arg = arg;
5 kx longjmp(jb, 1);
5 kx }
5 kx
5 kx static int
5 kx internettime (struct timehost *thishost)
5 kx {
5 kx register struct hostent *hp;
5 kx struct sockaddr_in sin;
5 kx long port;
5 kx int nread;
5 kx static int s = -1;
5 kx
5 kx if (thishost -> local) {
5 kx if (gettimeofday (&now, (struct timezone *)0) == -1) {
5 kx perror ("netdate: gettimeofday");
5 kx exit (1);
5 kx }
5 kx thishost -> asked = now;
5 kx thishost -> then = now;
5 kx thishost -> acked = now;
5 kx return(1);
5 kx }
5 kx timerclear(&thishost -> then);
5 kx if (setjmp(jb))
5 kx goto bad;
5 kx (void)signal(SIGALRM, timeout);
5 kx if (s != -1)
5 kx (void) close (s), s = -1;
5 kx port = getport(thishost -> protoname);
5 kx bzero((char *)&sin, sizeof (sin));
5 kx sethostent(1);
5 kx if ((hp = gethostbyname(thishost -> hostname)) == NULL) {
5 kx fprintf(stderr, "%s: %s: unknown host\n",
5 kx whoami, thishost -> hostname);
5 kx goto out;
5 kx }
5 kx sin.sin_family = hp->h_addrtype;
5 kx (void)alarm(20);
5 kx s = socket(hp->h_addrtype, thishost -> socktype, 0 /*protonumber*/);
5 kx if (s < 0) {
5 kx perror("netdate: socket");
5 kx (void)alarm(0);
5 kx goto out;
5 kx }
5 kx if (thishost -> socktype == SOCK_STREAM) {
5 kx if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
5 kx perror("netdate: bind");
5 kx goto bad;
5 kx }
5 kx }
5 kx bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
5 kx sin.sin_port = port;
5 kx (void)gettimeofday (&thishost -> asked, (struct timezone *)0);
5 kx if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
5 kx perror("netdate: connect");
5 kx goto bad;
5 kx }
5 kx
5 kx if (thishost -> socktype == SOCK_DGRAM) {
5 kx if (send (s, "\n", 1, 0) < 0) {
5 kx perror ("netdate: send");
5 kx goto bad;
5 kx }
5 kx }
5 kx nread = recv (s, (char *)&thishost -> then, sizeof (thishost -> then), 0);
5 kx (void)gettimeofday (&thishost -> acked, (struct timezone *)0);
5 kx (void)alarm(0);
5 kx now = thishost -> acked;
5 kx
5 kx if (nread != 4) {
5 kx perror ("netdate: read");
5 kx goto bad;
5 kx }
5 kx
5 kx /* RFC 868 only allows seconds, but what the hell */
5 kx if (nread == sizeof(thishost -> then))
5 kx thishost -> then.tv_usec = ntohl(thishost -> then.tv_usec);
5 kx else
5 kx thishost -> then.tv_usec = 0L;
5 kx thishost -> then.tv_sec = ntohl (thishost -> then.tv_sec) - NETBASE;
5 kx return (1); /* don't close before returning to avoid delays */
5 kx bad:
5 kx (void)alarm(0);
5 kx (void) close (s), s = -1;
5 kx out:
5 kx if (gettimeofday (&now, (struct timezone *)0) == -1) {
5 kx perror ("netdate: gettimeofday");
5 kx exit (1);
5 kx }
5 kx thishost -> asked = now;
5 kx thishost -> then = now;
5 kx thishost -> acked = now;
5 kx thishost -> bad = 1;
5 kx fprintf (stderr, "Connection with %s to %s failed.\n",
5 kx thishost -> protoname, thishost -> hostname);
5 kx return(0);
5 kx }
5 kx
5 kx long
5 kx getport(char *protoname)
5 kx {
5 kx register struct servent *sp;
5 kx static long port;
5 kx
5 kx if (port != 0)
5 kx return(port);
5 kx if ((sp = getservbyname(service, protoname)) == 0) {
5 kx fprintf(stderr, "%s: %s/%s: unknown service\n",
5 kx whoami, service, protoname);
5 kx exit(1);
5 kx }
5 kx return (port = sp->s_port);
5 kx }