home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / N / TCPIP / NETDATE-.16 / NETDATE- / netdate / netdate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-19  |  12.3 KB  |  502 lines

  1. /* 
  2.                             NO WARRANTY
  3.  
  4.     THERE IS NO WARRANTY FOR THIS PROGRAM, TO THE EXTENT PERMITTED BY
  5.   APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
  6.   HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
  7.   OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
  8.   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  9.   PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
  10.   IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
  11.   ALL NECESSARY SERVICING, REPAIR OR CORRECTION.  
  12.                               
  13.   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
  14.   ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
  15.   REDISTRIBUTE THE PROGRAM, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
  16.   GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
  17.   USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
  18.   DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
  19.   PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
  20.   EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
  21.   SUCH DAMAGES.  
  22.                                 
  23. */
  24.  
  25. #ifndef lint
  26.     char sccsid[]="@(#) netdate.c 1.16 85/08/21";
  27. #endif
  28. #include <sys/param.h>
  29. #include <sys/stat.h>
  30. #include <sys/ioctl.h>
  31. #include <sys/socket.h>
  32. #include <sys/time.h>
  33.  
  34. #include <netinet/in.h>
  35.  
  36. #include <fcntl.h>
  37. #include <string.h>
  38. #include <stdlib.h>
  39. #include <unistd.h>
  40. #include <stdio.h>
  41. #include <netdb.h>
  42. #include <setjmp.h>
  43. #include <signal.h>
  44. #include <utmp.h>
  45. #define WTMP "/etc/wtmp"
  46.  
  47. struct utmp wtmp[2] = {
  48.     { 0, 0, "|", "", 0, "", "", 0},
  49.     { 0, 0, "{", "", 0, "", "", 0}
  50. };
  51.  
  52. char *service = "time";
  53. char *defaultproto = "udp";
  54. /* difference between 1900 (RFC868) and 1970 (UNIX) base times */
  55. #define NETBASE    2208988800
  56.  
  57. long limit = 5;
  58. #define MAXHOSTS 20
  59.  
  60. #define LOCALHOST "localhost"
  61. char *whoami;
  62. char hostname[65];
  63. struct timeval now;
  64. struct timehost {
  65.     char *hostname;
  66.     short local;
  67.     short bad;
  68.     char *protoname;
  69.     long protonumber;
  70.     int socktype;
  71.     struct timeval asked;
  72.     struct timeval then;
  73.     struct timeval acked;
  74.     long difference;
  75.     long count;
  76. } timehosts[MAXHOSTS];
  77. struct timehost *tophost = &timehosts[MAXHOSTS];
  78.  
  79. void    usage (void);
  80. int    setproto (char *, struct timehost *);
  81. void    main (int, char **);
  82. int    getdiff (struct timehost *);
  83. int    getdate (struct timehost *);
  84. void    printit (struct timehost *);
  85. void    tvsub (struct timeval *, struct timeval *, struct timeval *);
  86. void    printdiff (char *, struct timeval *);
  87. void    timeout ();
  88. static    internettime (struct timehost *thishost);
  89. long    getport (char *protoname);
  90.  
  91. static int internettime (struct timehost *thishost);
  92. struct timehost *mungediffs(struct timehost *);
  93.  
  94.  
  95. void
  96. usage (void)
  97. {
  98.     fprintf (stderr,
  99. "usage: %s [ -l limit ] host ...\n"
  100. "%s tries to find a group of at least two hosts whose times agree\n"
  101. "within %d seconds, and sets the time to that of the first host in the group.\n",
  102.         whoami, whoami, limit);
  103.     fprintf (stderr,
  104. "The limit may be set with the -l option.  Setting it to zero (or supplying\n"
  105. "only one host name argument) will set the time to that of the first host to\n"
  106. "respond. The caller must be super-user for the system time to be set.\n");
  107.  
  108.     exit (1);
  109. }
  110.  
  111. int rdate = 0;
  112. int verbose = 0;
  113. int debug = 0;
  114.  
  115. void
  116. main (int argc, char **argv)
  117. {
  118.     extern char *rindex();
  119.     struct timehost *mungediffs();
  120.     register struct timehost *thishost;
  121.     int hostargs = 0;
  122.  
  123.     if ((whoami = rindex(*argv, '/')) != NULL)
  124.         whoami++;
  125.     else
  126.         whoami = *argv;
  127.     if (strcmp (whoami, "rdate") == 0) {    /* emulate SMI rdate command */
  128.         rdate = 1;
  129.         defaultproto = "tcp";
  130.         limit = 0;
  131.     }
  132.     if (gethostname(hostname, (int)sizeof (hostname)) == -1) {
  133.         perror ("gethostname");
  134.         exit (1);
  135.     }
  136.     while (*++argv != NULL && **argv == '-') {
  137.         switch (argv[0][1]) {
  138.         case 'd':
  139.             debug++;
  140.             break;
  141.         case 'v':
  142.             verbose++;
  143.             break;
  144.         case 'l':
  145.             if (*++argv == NULL)
  146.                 usage();
  147.             limit = atoi(*argv);
  148.             break;
  149.         default:
  150.             fprintf (stderr, "Unknown option:  %s\n", *argv);
  151.             usage();
  152.             break;
  153.         }
  154.     }
  155.     if (*argv == NULL)
  156.         usage();
  157.     if (debug)
  158.         fprintf (stderr, "%s: rdate %d; verbose %d; limit %d.\n", 
  159.             whoami, rdate, verbose, limit);
  160.     for (thishost = &timehosts[0]; *argv != NULL; argv++) {
  161.         if (thishost >= tophost) {
  162.             fprintf(stderr, "Too many hosts: ignoring");
  163.             do {
  164.                 fprintf (stderr, " %s", *argv);
  165.             } while (*++argv != NULL);
  166.             fprintf (stderr, "\n");
  167.             break;
  168.         }
  169.         if (setproto(*argv, thishost))
  170.             continue;
  171.         thishost -> hostname = *argv;
  172.         thishost -> bad = 0;
  173.         if (strcmp (thishost -> hostname, LOCALHOST) == 0)
  174.             thishost -> local = 1;
  175.         if (++hostargs == 1 && argv[1] == NULL)    /* Only one host arg, */
  176.             limit = 0;            /* so just set to it. */
  177.         if (limit == 0) {
  178.             if (!getdate(thishost))
  179.                 continue;
  180.             exit(0);
  181.         }
  182.         if (!getdiff (thishost))
  183.             continue;
  184.         thishost++;
  185.     }
  186.     if (limit == 0)
  187.         exit(1);
  188.     if (thishost == &timehosts[0])
  189.         exit(1);
  190.     if ((thishost = mungediffs(thishost)) == NULL) {
  191.         fprintf (stderr,
  192.             "No two hosts agree on the time within %d seconds\n",
  193.             limit);
  194.         exit(1);
  195.     }
  196.     if (!getdate (thishost))
  197.         exit (1);
  198.     exit(0);
  199. }
  200.  
  201. int
  202. setproto(char *what, struct timehost *thishost)
  203. {
  204.     static    char *protoname;
  205.     static    long protonumber;
  206.     static    int socktype;
  207.     register struct protoent *pp;
  208.  
  209.     setprotoent(1);
  210.     if ((pp = getprotobyname (what)) == NULL) {
  211.         if (protoname == NULL)
  212.             if (!setproto(defaultproto, thishost)) {
  213.                 fprintf(stderr,
  214.         "Default protocol %s was not found in /etc/protocols.\n",
  215.                         defaultproto);
  216.                 exit(1);
  217.             }
  218.         thishost -> protoname = protoname;
  219.         thishost -> protonumber = protonumber;
  220.         thishost -> socktype = socktype;
  221.         return(0);
  222.     }
  223.     protoname = what;    /*pp -> p_name;    this is static:  don't use it.*/
  224.     protonumber = pp -> p_proto;
  225.     switch (protonumber) {
  226.         case IPPROTO_TCP:
  227.             socktype = SOCK_STREAM;
  228.             if (debug)
  229.                 fprintf(stderr, "%s SOCK_STREAM\n", protoname);
  230.             break;
  231.         case IPPROTO_UDP:
  232.             socktype = SOCK_DGRAM;
  233.             if (debug)
  234.                 fprintf(stderr, "%s SOCK_DGRAM\n", protoname);
  235.             break;
  236.         default:
  237.             fprintf(stderr, "Unknown protocol:  %s\n", protoname);
  238.             exit(1);
  239.             break;
  240.     }
  241.     return(1);
  242. }
  243.  
  244. int
  245. getdiff(struct timehost *thishost)
  246. {
  247.     if (!internettime (thishost))
  248.         return(0);
  249.     thishost -> difference = thishost -> then.tv_sec - now.tv_sec;
  250.     if (!rdate)
  251.         printit(thishost);
  252.     return(1);
  253. }
  254.  
  255.  
  256. /*
  257.     Find the largest group of hosts which agree within the limit
  258.     and return the first of that group.  If no two hosts agree,
  259.     give up.
  260.  */
  261.  
  262. struct timehost *
  263. mungediffs(struct timehost *tophost)
  264. {
  265.     register struct timehost *thishost, *ahost, *goodhost;
  266.     long diff;
  267.  
  268.     tophost--;    /* simplifies the comparisons */
  269.     goodhost = &timehosts[0];
  270.     for (thishost = &timehosts[0]; thishost < tophost; thishost++) {
  271.         if (thishost -> bad)
  272.             continue;
  273.         thishost -> count = 1;
  274.         if (verbose)
  275.             printf ("%s", thishost -> hostname);
  276.         for (ahost = thishost + 1; ahost <= tophost; ahost++) {
  277.             if (thishost -> bad)
  278.                 continue;
  279.             diff = ahost -> difference - thishost -> difference;
  280.             if (abs(diff) < limit) {
  281.                 thishost -> count++;
  282.                 if (verbose)
  283.                     printf (" %s", ahost -> hostname);
  284.             }
  285.         }
  286.         if (verbose) {
  287.             printf (" %d\n", thishost -> count);
  288.             (void)fflush(stdout);
  289.         }
  290.         if (thishost -> count > goodhost -> count)
  291.             goodhost = thishost;
  292.     }
  293.     if (goodhost -> count > 1)
  294.         return(goodhost);
  295.     return(NULL);
  296. }
  297.  
  298. int
  299. getdate (struct timehost *thishost)
  300. {
  301.     int set = 0;
  302.  
  303.     if (!internettime (thishost))
  304.         return (0);
  305.     if (thishost -> local) {
  306.         printf ("Local host %s has best time, so not setting date\n",
  307.             hostname);
  308.         printit(thishost);
  309.         exit(0);
  310.     }
  311.     if (limit != 0
  312.     && abs((thishost -> then.tv_sec - now.tv_sec) - thishost -> difference)
  313.         > limit) {
  314.         fprintf (stderr,
  315.         "Time from %s has varied more than the limit of %d seconds\n",
  316.             thishost -> hostname, limit);
  317.         printit(thishost);
  318.         exit(1);
  319.     }
  320.     if (settimeofday (&thishost -> then, (struct timezone *)0) == -1)
  321.         perror ("netdate; settimeofday");
  322.     else {
  323.         int wf;
  324.         if ((wf = open(WTMP, 1)) >= 0) {
  325.             wtmp[0].ut_time = now.tv_sec;
  326.             wtmp[1].ut_time = thishost -> then.tv_sec;
  327.             (void)lseek(wf, 0L, 2);
  328.             (void)write(wf, (char *)wtmp, sizeof(wtmp));
  329.             (void)close(wf);
  330.         }
  331.         set = 1;
  332.     }
  333.     printit(thishost);
  334.     return(set);
  335. }
  336.  
  337. void
  338. printit(struct timehost *thishost)
  339. {
  340.     extern char *ctime();
  341.     struct tm *tp, *localtime();
  342.     struct timeval diff;
  343.     char newstring[128];
  344.  
  345.     if (rdate)
  346.         printf ("%s", ctime((unsigned long *)&thishost -> then.tv_sec));
  347.     else {
  348.         (void)sprintf(newstring, "%s ", thishost -> hostname);
  349.         tvsub(&diff, &thishost -> then, &now);
  350.         printdiff(&newstring[strlen(newstring)], &diff);
  351.         printf ("%-24s %.19s.%03d", newstring,
  352.             ctime((unsigned long *)&thishost -> then.tv_sec),
  353.                 thishost -> then.tv_usec / 1000);
  354.         if (verbose) {
  355.             tp = localtime((unsigned long *)&thishost -> acked);
  356.             printf(" at %02d:%02d:%02d.%03d",
  357.                 tp -> tm_hour, tp -> tm_min, tp -> tm_sec,
  358.                 thishost -> acked.tv_usec / 1000);
  359.             tvsub(&diff, &thishost -> acked, &thishost -> asked);
  360.             printdiff(newstring, &diff);
  361.             printf(" delay %s", newstring);
  362.         }
  363.         printf("\n");
  364.     }
  365.     (void)fflush (stdout);
  366. }
  367.  
  368. void
  369. tvsub(tdiff, t1, t0)
  370. struct timeval *tdiff, *t1, *t0;
  371. {
  372.     tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
  373.     tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
  374.     if (tdiff->tv_sec < 0 && tdiff->tv_usec > 0)
  375.         tdiff->tv_sec++, tdiff->tv_usec -= 1000000;
  376.     if (tdiff->tv_sec > 0 && tdiff->tv_usec < 0)
  377.         tdiff->tv_sec--, tdiff->tv_usec += 1000000;
  378. }
  379.  
  380. void
  381. printdiff(char *where, struct timeval *diff)
  382. {
  383.     (void) sprintf (where, "%c%d.%.03d",
  384.         (diff->tv_sec < 0 || diff->tv_usec < 0) ? '-' : '+',
  385.         abs(diff->tv_sec), abs(diff->tv_usec) / 1000);
  386. }
  387.  
  388. static    jmp_buf jb;
  389. void
  390. timeout()
  391. {
  392.     longjmp(jb, 1);
  393. }
  394.  
  395. static int
  396. internettime (struct timehost *thishost)
  397. {
  398.     register struct hostent *hp;
  399.     struct sockaddr_in sin;
  400.     long port;
  401.     int nread;
  402.     static int s = -1;
  403.  
  404.     if (thishost -> local) {
  405.         if (gettimeofday (&now, (struct timezone *)0) == -1) {
  406.             perror ("netdate: gettimeofday");
  407.             exit (1);
  408.         }
  409.         thishost -> asked = now;
  410.         thishost -> then = now;
  411.         thishost -> acked = now;
  412.         return(1);
  413.     }
  414.     timerclear(&thishost -> then);
  415.     if (setjmp(jb))
  416.         goto bad;
  417.     (void)signal(SIGALRM, timeout);
  418.     if (s != -1)
  419.         (void) close (s), s = -1;
  420.     port = getport(thishost -> protoname);
  421.     bzero((char *)&sin, sizeof (sin));
  422.     sethostent(1);
  423.     if ((hp = gethostbyname(thishost -> hostname)) == NULL) {
  424.         fprintf(stderr, "%s: %s: unknown host\n",
  425.             whoami, thishost -> hostname);
  426.         goto out;
  427.     }
  428.     sin.sin_family = hp->h_addrtype;
  429.     (void)alarm(20);
  430.     s = socket(hp->h_addrtype, thishost -> socktype, 0 /*protonumber*/);
  431.     if (s < 0) {
  432.         perror("netdate: socket");
  433.         (void)alarm(0);
  434.         goto out;
  435.     }
  436.     if (thishost -> socktype == SOCK_STREAM) {
  437.         if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
  438.             perror("netdate: bind");
  439.             goto bad;
  440.         }
  441.     }
  442.     bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
  443.     sin.sin_port = port;
  444.     (void)gettimeofday (&thishost -> asked, (struct timezone *)0);
  445.     if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
  446.         perror("netdate: connect");
  447.         goto bad;
  448.     }
  449.     if (thishost -> socktype == SOCK_DGRAM) {
  450.         if (write (s, "\n", 1) < 0) {
  451.             perror ("netdate: send");
  452.             goto bad;
  453.         }
  454.     }
  455.     nread = read (s, (char *)&thishost -> then, sizeof (thishost -> then));
  456.     (void)gettimeofday (&thishost -> acked, (struct timezone *)0);
  457.     (void)alarm(0);
  458.     now = thishost -> acked;
  459.     if (nread < sizeof(thishost -> then.tv_sec)) {
  460.         perror ("netdate: read");
  461.         goto bad;
  462.     }
  463.     /* RFC 868 only allows seconds, but what the hell */
  464.     if (nread == sizeof(thishost -> then))
  465.         thishost -> then.tv_usec = ntohl(thishost -> then.tv_usec);
  466.     else
  467.         thishost -> then.tv_usec = 0L;
  468.     thishost -> then.tv_sec = ntohl (thishost -> then.tv_sec) - NETBASE;
  469.     return (1);    /* don't close before returning to avoid delays */
  470. bad:
  471.     (void)alarm(0);
  472.     (void) close (s), s = -1;
  473. out:
  474.     if (gettimeofday (&now, (struct timezone *)0) == -1) {
  475.         perror ("netdate: gettimeofday");
  476.         exit (1);
  477.     }
  478.     thishost -> asked = now;
  479.     thishost -> then = now;
  480.     thishost -> acked = now;
  481.     thishost -> bad = 1;
  482.     fprintf (stderr, "Connection with %s to %s failed.\n",
  483.         thishost -> protoname, thishost -> hostname);
  484.     return(0);
  485. }
  486.  
  487. long
  488. getport(char *protoname)
  489. {
  490.     register struct servent *sp;
  491.     static long port;
  492.  
  493.     if (port != 0)
  494.         return(port);
  495.     if ((sp = getservbyname(service, protoname)) == 0) {
  496.         fprintf(stderr, "%s: %s/%s: unknown service\n",
  497.             whoami, service, protoname);
  498.         exit(1);
  499.     }
  500.     return (port = sp->s_port);
  501. }
  502.