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