home *** CD-ROM | disk | FTP | other *** search
/ nisttime.carsoncity.k12.mi.us / nisttime.carsoncity.k12.mi.us.tar / nisttime.carsoncity.k12.mi.us / pub / daytime / t_ping.c < prev    next >
C/C++ Source or Header  |  2004-05-03  |  15KB  |  420 lines

  1. #include <sys/time.h>
  2. main (argc,argv)
  3. int argc;
  4. char *argv[];
  5. {
  6. /*
  7.     This program estimates the time difference between the
  8.     local clock and time time of the server using an icmp
  9.     ping request.  
  10.  
  11.     It estimates the network delay to the server and uses
  12.     this delay to correct the measured time difference.
  13.  
  14.     This software was developed with US Government support
  15.     and it may not be sold, restricted or licensed.  You 
  16.     may duplicate this program provided that this notice
  17.     remains in all of the copies, and you may give it to
  18.     others provided they understand and agree to this
  19.     condition.
  20.  
  21.     This program and the time protocol it uses are under 
  22.     development and the implementation may change without 
  23.     notice.
  24.  
  25.     For questions or additional information, contact:
  26.  
  27.     Judah Levine
  28.     Time and Frequency Division
  29.     NIST/847
  30.     325 Broadway
  31.     Boulder, Colorado 80305
  32.     (303) 492 7785
  33.     jlevine@boulder.nist.gov
  34. */
  35. #include <stdio.h>
  36. #include <string.h>
  37. #include <sys/types.h>
  38. #include <sys/socket.h>
  39. #include <sys/file.h>
  40. #include <sys/param.h>
  41. #include <netinet/in_systm.h>
  42. #include <netinet/in.h>
  43. #include <netinet/ip.h>
  44. #include <netinet/ip_icmp.h>
  45. #include <netdb.h>
  46. long int address;        /* holds ip address */
  47. char *cp;            /* server address in . notation */
  48. char *sp;            /* temporary for server address*/
  49. int aindots[4];                 /* numerical host address in dot notation*/
  50. char addrbuf[20];               /* address formatted into dot notation*/
  51. char rp[20];            /* reply address */
  52. struct sockaddr_in sin;        /* socket address structure for outbound msg*/
  53. struct sockaddr_in from;    /* socket address structure for reply*/
  54. int fromlen;            /* returned size of from socket */
  55. struct protoent *proto;        /* protocol structure */
  56. struct icmp *icp;        /* icmp packet header */
  57. int s;                /* socket number */
  58. int length;            /* size of total reply message */
  59. int icmplen;            /* size of icmp reply*/
  60. unsigned char obuf[200];    /* holds outgoing message */
  61. unsigned char ibuf[100];    /* holds reply datagram*/
  62. unsigned long sendtime;        /* local send time */
  63. unsigned long recdtime;        /* distant received time */
  64. unsigned long trantime;        /* distant transmit time*/
  65. unsigned long rec2time;        /* local receive time*/
  66. int j;                /* gotta have one of these */
  67. long int cksum;            /* packet check sum */
  68. unsigned short int *intptr;        /* pointer to 16-bit parts of packet*/
  69. unsigned short int recident;    /* received identifier */
  70. unsigned long mstime();        /* time of day in msec since 0000 */
  71. long int msdelay;        /* one way delay in msec */
  72. long int diff,diff1,diff2;    /* time difference estimates, local - remote*/
  73. int debug = 0;            /* print out intermediate values if != 0*/
  74. struct timeval tvv;        /* used to adjust local time*/
  75. char cc;
  76. int sw();            /* parses command line switch*/
  77. char let;            /* switch letter*/
  78. long int val;            /* switch value*/
  79. struct hostent *serv0;          /*pointer to structure returned by gethost */
  80. int ierr=0;            /* consecutive error counter*/
  81. /*
  82. Command line switch:
  83.  
  84.     -d        turn on debug mode
  85.  
  86.         -u<j>           use server number j, where j is the index number
  87.                         of the NIST server chosen from the following list.
  88.                         the first server on the list is number 1, the
  89.                         second is number 2, etc.
  90.                         the default is to use the first server, which
  91.                         is number 1.
  92.                         note that the array uses C indexing, so that the
  93.                         first entry in the array is number 0, etc.
  94.                         thus any user response >0 is decremented before
  95.                         being used.
  96.  
  97.         -u0             the name of the server is given as the next
  98.                         parameter on the command line. the name can
  99.                         be either a name or an ip address in dot
  100.                         notation. The entry will be taken as a name
  101.                         if the first non-blank character is not a
  102.                         digit.
  103. */
  104. int use_serv= 0;    /*use first server by default*/
  105. /*
  106.         the following is a list of the servers operated by
  107.         NIST. All of them will support the daytime protocol
  108.         in the format that this program expects.
  109.  
  110.         Each server may be specified either by name, as in
  111.         time.nist.gov or by ip as in 192.43.244.18. If the
  112.         first character of the specification is a digit, then
  113.         the numerical format is assumed. If a name is entered,
  114.         it is converted to the corresponding ip address using
  115.         the standard DNS query. The program will fail if the
  116.         DNS query cannot find the ip number of the server
  117.  
  118.         For more information about these servers, look at the
  119.         entry for the Internet Time Service on the Time and
  120.         Frequency Division homepage at www.boulder.nist.gov/timefreq
  121. */
  122. #define NUMSRV 15
  123. char *serv_ip[NUMSRV]= {"64.236.96.53"   ,/*nist1.aol-va.truetime.com*/
  124.                         "128.138.140.44" ,/*utcnist.colorado.edu*/
  125.                         "207.200.81.113" ,/*nist1.aol-ca.truetime.com*/
  126.                         "216.200.93.8"   ,/*nist1-dc.glassey.com*/
  127.                         "63.149.208.50"  ,/*nist1.datum.com*/
  128.                         "208.184.49.9"   ,/*nist1-ny.glassey.com*/
  129.                         "207.126.103.204",/*nist1-sj.glassey.com*/
  130.                         "129.6.15.28"    ,/*time-a.nist.gov*/
  131.                         "129.6.15.29"    ,/*time-b.nist.gov*/
  132.                         "132.163.4.101"  ,/*time-a.timefreq.bldrdoc.gov*/
  133.                         "132.163.4.102"  ,/*time-b.timefreq.bldrdoc.gov*/
  134.                         "132.163.4.103"  ,/*time-c.timefreq.bldrdoc.gov*/
  135.                         "192.43.244.018" ,/*time.nist.gov*/
  136.                         "131.107.1.10"   , /*time-nw.nist.gov*/  
  137.             "utcnist.colorado.edu" /*DNS entry=128.138.140.44*/
  138.             };
  139. /*
  140.         parse command line
  141. */
  142.         while( sw(argc,argv,&let,&val) != 0) /*switch is present*/
  143.            {
  144.            switch(let)
  145.               {
  146.           case 'd':
  147.         debug=1;
  148.         break;
  149.               case 'u':
  150.                if(val == 0)    /*next parameter specifies server*/
  151.                    {
  152.                    argc--;
  153.                    argv++;      /*skip over the switch*/
  154.                    if(argc <= 1)
  155.                         {
  156.                         printf("\n Expected server name is missing.\n\n");
  157.                         exit();
  158.                    }
  159.                    cp=argv[1];  /*save the next parameter as the server name*/
  160.                    use_serv=999;        /*set flag*/
  161.                    break;
  162.                 }
  163.                 /*  server 1 has internal index 0 */
  164.                 use_serv=val-1;
  165.                 /*  check if entry is out of range*/
  166.                 if(use_serv < 0) use_serv=0;
  167.                 if(use_serv >= NUMSRV) use_serv=NUMSRV - 1;
  168.                 break;
  169.               default:
  170.                 fprintf(stderr,"\nSwitch %c not recognized.",let);
  171.                 break;
  172.            }
  173.            argc--;      /*decrement argument counter */
  174.            argv++;      /*and increment pointer */
  175.         }
  176. /*
  177.         get internet address of selected server
  178.         if the first digit is not a digit then lookup
  179.         the host name and get its ip address. the program
  180.         will fail and exit if the name cannot be resolved.
  181. */
  182.     if(use_serv != 999) cp=serv_ip[use_serv];
  183.         if(!isdigit(*cp))       /*first char not a digit, must convert name*/
  184.            {
  185.            if( (serv0=gethostbyname(cp)) == NULL)
  186.                 {
  187.                 printf("\n Cannot resolve name %s",cp);
  188.                 exit();
  189.            }
  190.            if(serv0->h_length != 4)
  191.               {
  192.               printf("\nLength of host address (= %d) is wrong, 4 expected.",
  193.                         serv0->h_length);
  194.               exit();
  195.            }
  196.            sp= serv0->h_addr_list[0];
  197.            for(j=0; j<4; j++)   /*store and convert address*/
  198.               {
  199.               aindots[j]= *(sp++);
  200.               aindots[j] &= 0xff;       /*turn off sign extension*/
  201.            }
  202.            sprintf(addrbuf,"%d.%d.%d.%d",
  203.                 aindots[0],aindots[1],aindots[2],aindots[3]);
  204.        cp= addrbuf;
  205.         }
  206.     printf("\n Using server at address %s",cp);
  207. /*
  208.     convert address to internal format
  209. */
  210.     if( (address=inet_addr(cp) ) == -1)
  211.        {
  212.        perror(stderr,"\n Internet address error. \n");
  213.        exit(1);
  214.     }
  215.     bzero( (char *)&sin,sizeof(sin));
  216.     sin.sin_addr.s_addr=address;
  217.     sin.sin_family=AF_INET;
  218. /*
  219.     get protocol number for icmp
  220. */
  221.     if( (proto=getprotobyname("icmp")) == NULL)
  222.        {
  223.        perror("\n can not find protocol name \n");
  224.        exit();
  225.     }
  226. /*
  227.     create the socket and then build the packet to send
  228.     note that this is a raw socket since we are going
  229.     to connect directly using  ICMP
  230.     This socket may require privilege.
  231. */
  232.     if( (s=socket(AF_INET,SOCK_RAW,proto->p_proto) ) < 0)
  233.        {
  234.        perror("Socket creation error.");
  235.        exit(1);
  236.     }
  237. /*
  238.     build the packet in array obuf using
  239.     structure pointers
  240.     packet header is 8 bytes followed by
  241.     optional data.  The type must be 13 to signal
  242.     time request.  The checksum is calculated later
  243.     and the ident field is our process id.  This field
  244.     is not checked at either end and could be anything.
  245.     The pid is used simply because it is an integer unique
  246.     to this process.  Thus two copies of this program won't 
  247.     interfere if they run at the same time.
  248.     The first thing in the optional
  249.     data is the local time stamp which starts at
  250.     byte 8 in the array.  The time is 4 bytes long
  251.     and is in units of milliseconds since 0000 today.
  252.     no date information is included in either the 
  253.     transmitted message or in the reply.  Thus this idea
  254.     could have a problem if the echo process is sent
  255.     just before 0000 and received just after 0000
  256. */
  257.     icp = (struct icmp *)obuf;
  258.     icp->icmp_type=13;   /* time req. or ICMP_ECHO  */
  259.     icp->icmp_code=0;
  260.     icp->icmp_cksum=0;
  261.     icp->icmp_id=getpid() & 0xffff;
  262.     icp->icmp_seq=0;
  263.     sendtime=mstime();    /*get current time*/
  264. /*
  265.     the local time is stored in bytes 7 - 11.  The
  266.     following loop moves the time into the output buffer
  267.     and automatically puts it into network order at
  268.     the same time.
  269. */
  270.     for(j=11; j>7; j--)        /*store it in bytes 8 - 11*/
  271.        {
  272.        obuf[j]= sendtime &0xff;
  273.        sendtime= sendtime >> 8;
  274.     }
  275.     for(j=16; j<64; j++) obuf[j]=0;    /*clear out rest of packet*/
  276. /*
  277.     compute the checksum -- add the contents of the packet
  278.     as 8 16 bit words -- the remainder of the packet is all
  279.     0 and need not be summed.
  280. */
  281.     cksum = 0;
  282.     intptr= (unsigned short int *) &obuf[0];
  283.     for(j=0; j<8; j++) cksum += *intptr++;
  284. /*
  285.     add high 16 bits of checksum to low 16 bits
  286.     If this operation resulted in a carry then
  287.     add the carry as well.
  288. */
  289.     cksum= (cksum >> 16) + (cksum & 0xffff);
  290.     cksum += (cksum >> 16);
  291.     cksum = ~cksum;        /* 1s complement*/
  292.     icp->icmp_cksum= (unsigned short int) (cksum & 0xffff);
  293. /*
  294.     okay folks, mail it off ..
  295. */
  296.     j=sendto(s,obuf,64,0,(struct sockaddr *) &sin,sizeof(sin));
  297.     if(j < 0) perror("sendto error ");
  298.     if(j != 64) printf("\n sent 64 bytes, sendto return is %d",j);
  299. /*
  300.     and see what comes back
  301. */
  302. rdone:        /* read another packet */
  303.     length=recvfrom(s,ibuf,sizeof(ibuf),0,&from,&fromlen);
  304.     rec2time=mstime();    /*get time as soon as read completes*/
  305.     if(length <= 0) 
  306.        {
  307.        perror(" receive error ");
  308.        if(ierr++ > 5) exit();
  309.     }
  310.     if(debug != 0)
  311.        {
  312.        printf("\n received length is %d \n",length);
  313.        for(j=0; j<length; j++) printf(" %d",ibuf[j]);
  314. /*
  315.        make sure that this packet came from the time server
  316.        take the source address from the packet and convert
  317.        it to . notation and compare it with the address
  318.        pointed to by cp above.
  319. */
  320.        printf("\n packet came from: %u %u %u %u",ibuf[12],
  321.         ibuf[13],ibuf[14],ibuf[15]);
  322.     }
  323.     sprintf(rp,"%d.%d.%d.%d",ibuf[12],ibuf[13],
  324.             ibuf[14],ibuf[15]);
  325.     if(strcmp(rp,cp) != 0)
  326.        {
  327.        printf("\n Packet did not come from time server.");
  328.        printf("\n received ip= %s",rp);
  329.        printf(" expected ip= %s",cp);
  330.        if(ierr++ > 5) exit();
  331.        goto rdone;        /* read another one */
  332.     }
  333.     icmplen=4*(ibuf[0] & 0xf);    /*length of icmp header*/
  334.     if(debug != 0)
  335.        {
  336.        printf("\n ip header length is %d bytes \n",icmplen);
  337.        for(j=icmplen; j<length; j++) printf(" %d",ibuf[j]);
  338.     }
  339. /*
  340.     now check to be sure that the identifier matches
  341. */
  342.     recident= ibuf[icmplen+4];
  343.     recident= (recident << 8) + ibuf[icmplen+5];
  344.     if(ntohs(recident) !=  icp->icmp_id)
  345.        {
  346.        if(debug != 0) printf("\n Identifer %d not as expected.",j);
  347.        goto rdone;
  348.     }
  349.     close(s);    /*this is the correct packet to parse*/
  350. /*
  351.     our orignate time stamp is in 8-11
  352.  
  353.     time stamp when it got to server is in 12 -15
  354.  
  355.     time stamp when it left server is 16-19
  356. */
  357.     sendtime=ibuf[icmplen+8];
  358.     for(j=9; j<12; j++) sendtime= 
  359.         (sendtime << 8) + ibuf[icmplen+j];
  360.     recdtime=ibuf[icmplen+12];
  361.     for(j=13; j<16; j++) recdtime=
  362.         (recdtime << 8) + ibuf[icmplen+j];
  363.     trantime=ibuf[icmplen+16];
  364.     for(j=17; j<20; j++) trantime=
  365.         (trantime << 8) + ibuf[icmplen+j];
  366.     printf("\n packet was sent at            %lu",sendtime);
  367.     printf("\n received at server at         %lu",recdtime);
  368.     printf("\n transmitted from server at    %lu",trantime);
  369.     printf("\n echo received here at         %lu",rec2time);
  370. /*
  371.     network delay is assumed to be the same in both directions.
  372.     thus the one way delay is one-half of the local elapsed time
  373.     less the latency delay in the server
  374. */
  375.     msdelay= (rec2time - sendtime)/2 - (trantime - recdtime);
  376. /*
  377.     first difference estiamte uses outbound packet corrected
  378.     for delay.
  379.     second differences estiamte uses inbound packet also corrected
  380.     for delay.
  381. */
  382.     diff1= (sendtime-recdtime) + msdelay;
  383.     diff2= (rec2time-trantime) - msdelay;
  384.     printf("\n Network delay is %ld msec.",msdelay);
  385.     printf(" \n Time difference 1, Local - NIST=%ld msec.",diff1);
  386.     printf(" \n Time difference 2, Local - NIST=%ld msec.",diff2);
  387.     diff=(diff1+diff2)/2;
  388.     printf(" \n Average Time Difference        =%ld msec.",diff);
  389.     if(diff == 0)
  390.        {
  391.        printf("\n No adjustment is necessary.\n");
  392.        exit();
  393.     }
  394.     printf("\n Do you want to adjust the local clock ? [y/n] ");
  395.     cc=getchar();
  396.     if( (cc == 'y') || (cc == 'Y') )
  397.        {
  398.        tvv.tv_sec= -diff/1000;    /*convert msec to sec*/
  399.        tvv.tv_usec = -(diff%1000);
  400.        tvv.tv_usec *= 1000;        /*convert to usec */
  401.        j=adjtime(&tvv,(struct timeval *) 0 );
  402.        if(j == 0) printf("\n Adjustment was performed.\n");
  403.        else perror("Adjustment failed. ");
  404.     }
  405. }
  406. unsigned long int mstime()
  407. {
  408. /*
  409.     this subroutine returns the current system time expressed
  410.     as the number of milliseconds since 0000 of the current
  411.     day.  No date information is returned
  412. */
  413. struct timeval tvv;
  414. unsigned long int j;
  415.     gettimeofday(&tvv,(struct timezone *)0 );
  416.     j=tvv.tv_sec%86400;       /*get number of seconds since 0000 today*/
  417.     j= 1000*j + tvv.tv_usec/1000;  /*convert to milliseconds*/
  418.     return(j);
  419. }
  420.