1 #include <sys/types.h>
2 #include <sys/time.h>
3 #include <sys/param.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include "strerr.h"
7 #include "ip.h"
8 #include "str.h"
9 #include "byte.h"
10 #include "substdio.h"
11 #include "readwrite.h"
12 #include "select.h"
13 #include "scan.h"
14 #include "leapsecs.h"
15 #include "tai.h"
16 #include "taia.h"
17 
18 #define NTP_OFFSET 2208988790UL /* TAI64 baseline - NTP epoch */
19 /* TAI64 baseline is 1970-01-01 00:00:00 = 4000000000000000 TAI64 */
20 /* NTP epoch is 1900-01-01 00:00:10 = 3fffffff7c55818a TAI64 */
21 
ntp_taia(ntp,ta,flagleap)22 void ntp_taia(ntp,ta,flagleap)
23 unsigned char *ntp;
24 struct taia *ta;
25 int flagleap;
26 {
27   unsigned char buf[16];
28   struct tai t;
29   unsigned long u;
30   double z;
31 
32   u = (unsigned long) ntp[0];
33   u <<= 8; u += (unsigned long) ntp[1];
34   u <<= 8; u += (unsigned long) ntp[2];
35   u <<= 8; u += (unsigned long) ntp[3];
36   u -= NTP_OFFSET;
37 
38   /* safe to assume that now is past 1970 */
39 
40   buf[0] = 64;
41   buf[1] = 0;
42   buf[2] = 0;
43   buf[3] = 0;
44   buf[7] = u; u >>= 8;
45   buf[6] = u; u >>= 8;
46   buf[5] = u; u >>= 8;
47   buf[4] = u;
48 
49   tai_unpack(buf,&t);
50   leapsecs_add(&t,flagleap);
51   tai_pack(buf,&t);
52 
53   u = (unsigned long) ntp[4];
54   u <<= 8; u += (unsigned long) ntp[5];
55   u <<= 8; u += (unsigned long) ntp[6];
56   u <<= 8; u += (unsigned long) ntp[7];
57   z = u / 4294967296.0;
58 
59   z *= 1000000000.0;
60   u = z;
61   if (u > z) --u;
62   if (u > 999999999) u = 999999999;
63   z -= u;
64   buf[11] = u; u >>= 8;
65   buf[10] = u; u >>= 8;
66   buf[9] = u; u >>= 8;
67   buf[8] = u;
68 
69   z *= 1000000000.0;
70   u = z;
71   if (u > z) --u;
72   if (u > 999999999) u = 999999999;
73   buf[15] = u; u >>= 8;
74   buf[14] = u; u >>= 8;
75   buf[13] = u; u >>= 8;
76   buf[12] = u;
77 
78   taia_unpack(buf,ta);
79 }
80 
81 char outbuf[16];
82 substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof outbuf);
83 
84 #define FATAL "sntpclock: fatal: "
85 #define WARNING "sntpclock: warning: "
86 
die_usage()87 void die_usage()
88 {
89   strerr_die1x(100,"sntpclock: usage: sntpclock ip.ad.dr.ess");
90 }
91 
92 char *host;
93 struct ip_address ipremote;
94 struct sockaddr_in sa;
95 int s;
96 
97 unsigned char query[48];
98 unsigned char response[128];
99 
100 char initdeltaoffset[] = {0,0,0,0,0,2,163,0,0,0,0,0,0,0,0,0};
101 char initdeltamin[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
102 char initdeltamax[] = {0,0,0,0,0,5,70,0,0,0,0,0,0,0,0,0};
103 char initerrmin[] = {255,255,255,255,255,255,255,254,0,0,0,0,0,0,0,0};
104 char initerrmax[] = {0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0};
105 struct taia deltaoffset;
106 struct taia deltamin;
107 struct taia deltamax;
108 struct taia errmin;
109 struct taia errmax;
110 
111 struct taia ta0;
112 struct taia ta1;
113 struct taia taremote;
114 
115 struct taia temp1;
116 struct taia temp2;
117 
118 unsigned char adj[16];
119 
main(argc,argv)120 void main(argc,argv)
121 int argc;
122 char **argv;
123 {
124   struct timeval tvselect;
125   fd_set rfds;
126   char *x;
127   unsigned long u;
128   int r;
129   int loop;
130   struct timeval tvcookie;
131   int flagleap;
132 
133   taia_unpack(initdeltamin,&deltamin);
134   taia_unpack(initdeltamax,&deltamax);
135   taia_unpack(initdeltaoffset,&deltaoffset);
136   taia_unpack(initerrmin,&errmin);
137   taia_unpack(initerrmax,&errmax);
138 
139   if (leapsecs_init() == -1)
140     strerr_die2sys(111,FATAL,"unable to initialize leap seconds: ");
141 
142   host = argv[1];
143   if (!host) die_usage();
144   if (!str_diff(host,"0")) host = "127.0.0.1";
145   if (host[ip_scan(host,&ipremote)]) die_usage();
146 
147   s = socket(AF_INET,SOCK_DGRAM,0);
148   if (s == -1)
149     strerr_die2sys(111,FATAL,"unable to create socket: ");
150 
151   byte_zero(&sa,sizeof(sa));
152   byte_copy(&sa.sin_addr,4,&ipremote);
153   x = (char *) &sa.sin_port;
154   x[0] = 0;
155   x[1] = 123; /* NTP */
156   sa.sin_family = AF_INET;
157 
158   for (loop = 0;loop < 10;++loop) {
159     byte_zero(query,sizeof query);
160     query[0] = 27; /* client, NTP version 3 */
161     query[2] = 8;
162 
163     gettimeofday(&tvcookie,(struct timezone *) 0);
164     u = tvcookie.tv_sec + NTP_OFFSET;
165     query[43] = u; u >>= 8;
166     query[42] = u; u >>= 8;
167     query[41] = u; u >>= 8;
168     query[40] = u;
169     u = tvcookie.tv_usec;
170     query[45] = u; u >>= 8; /* deliberately inaccurate; this is a cookie */
171     query[44] = u;
172     u = getpid();
173     query[47] = u; u >>= 8;
174     query[46] = u;
175 
176     taia_now(&ta0);
177     if (sendto(s,query,sizeof query,0,(struct sockaddr *) &sa,sizeof sa) == -1)
178       strerr_die2sys(111,FATAL,"unable to send request: ");
179     FD_ZERO(&rfds);
180     FD_SET(s,&rfds);
181     tvselect.tv_sec = 1;
182     tvselect.tv_usec = 0;
183     if (select(s + 1,&rfds,(fd_set *) 0,(fd_set *) 0,&tvselect) != 1) {
184       strerr_warn2(WARNING,"unable to read clock: timed out",0);
185       continue;
186     }
187     r = recv(s,response,sizeof response,0);
188     if (r == -1) {
189       strerr_warn2(WARNING,"unable to read clock: ",&strerr_sys);
190       continue;
191     }
192     taia_now(&ta1);
193     if (   (r < 48)
194 	|| (r >= sizeof response)
195 	|| (((response[0] & 7) != 2) && ((response[0] & 7) != 4))
196 	|| !(response[0] & 56)
197 	|| byte_diff(query + 40,8,response + 24)
198        ) {
199       strerr_warn2(WARNING,"unable to read clock: bad response format",0);
200       continue;
201     }
202 
203     flagleap = ((response[0] & 192) == 64);
204 
205     ntp_taia(response + 32,&taremote,flagleap);
206     taia_add(&taremote,&taremote,&deltaoffset);
207 
208     taia_add(&temp1,&deltamax,&ta0);
209     taia_add(&temp2,&deltamin,&ta0);
210     if (taia_less(&taremote,&temp1) && !taia_less(&taremote,&temp2)) {
211       taia_sub(&temp1,&taremote,&ta0);
212       deltamax = temp1;
213     }
214 
215     ntp_taia(response + 40,&taremote,flagleap);
216     taia_add(&taremote,&taremote,&deltaoffset);
217 
218     taia_add(&temp1,&deltamax,&ta1);
219     taia_add(&temp2,&deltamin,&ta1);
220     if (taia_less(&temp2,&taremote) && !taia_less(&temp1,&taremote)) {
221       taia_sub(&temp2,&taremote,&ta1);
222       deltamin = temp2;
223     }
224   }
225 
226   taia_sub(&temp1,&deltamax,&deltamin);
227   if (taia_less(&errmax,&temp1) && taia_less(&temp1,&errmin))
228     strerr_die2x(111,FATAL,"time uncertainty too large");
229 
230   taia_add(&temp1,&deltamax,&deltamin);
231   taia_half(&temp1,&temp1);
232   taia_sub(&temp1,&temp1,&deltaoffset);
233 
234   taia_pack(adj,&temp1);
235 
236   if (substdio_putflush(&ssout,adj,sizeof adj) == -1)
237     strerr_die2sys(111,FATAL,"unable to write output: ");
238   _exit(0);
239 }
240