Get periodical time samples through NTP
[novacoin.git] / src / ntp.cpp
1
2 #ifdef WIN32
3 #include <winsock2.h>
4 #else
5 #include <sys/socket.h>
6 #include <sys/time.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <netdb.h>
10 #endif
11 #ifndef WIN32
12 #include <unistd.h>
13 #endif
14
15 #include "netbase.h"
16 #include "util.h"
17 #include "net.h"
18
19 extern int GetRandInt(int nMax);
20
21 /*
22  * NTP uses two fixed point formats.  The first (l_fp) is the "long"
23  * format and is 64 bits long with the decimal between bits 31 and 32.
24  * This is used for time stamps in the NTP packet header (in network
25  * byte order) and for internal computations of offsets (in local host
26  * byte order). We use the same structure for both signed and unsigned
27  * values, which is a big hack but saves rewriting all the operators
28  * twice. Just to confuse this, we also sometimes just carry the
29  * fractional part in calculations, in both signed and unsigned forms.
30  * Anyway, an l_fp looks like:
31  *
32  *    0                   1                   2                   3
33  *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
34  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35  *   |                         Integral Part                         |
36  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37  *   |                         Fractional Part                       |
38  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39  * REF http://www.eecis.udel.edu/~mills/database/rfc/rfc2030.txt
40  */
41
42
43 typedef struct {
44   union {
45     uint32_t Xl_ui;
46     int32_t Xl_i;
47   } Ul_i;
48   union {
49     uint32_t Xl_uf;
50     int32_t Xl_f;
51   } Ul_f;
52 } l_fp;
53
54
55 inline void Ntp2Unix(uint32_t &n, time_t &u) {
56     // Ntp's time scale starts in 1900, Unix in 1970.
57
58     u = n - 0x83aa7e80; // 2208988800 1970 - 1900 in seconds
59 }
60
61 inline void ntohl_fp(l_fp *n, l_fp *h) {
62     (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui);
63     (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf);
64 }
65
66 struct pkt {
67   uint8_t  li_vn_mode;     /* leap indicator, version and mode */
68   uint8_t  stratum;        /* peer stratum */
69   uint8_t  ppoll;          /* peer poll interval */
70   int8_t  precision;      /* peer clock precision */
71   uint32_t    rootdelay;      /* distance to primary clock */
72   uint32_t    rootdispersion; /* clock dispersion */
73   uint32_t refid;          /* reference clock ID */
74   l_fp    ref;        /* time peer clock was last updated */
75   l_fp    org;            /* originate time stamp */
76   l_fp    rec;            /* receive time stamp */
77   l_fp    xmt;            /* transmit time stamp */
78
79   uint32_t exten[1];       /* misused */
80   uint8_t  mac[5 * sizeof(uint32_t)]; /* mac */
81 };
82
83 int nServersCount = 107;
84
85 std::string NtpServers[107] = {
86     // Microsoft
87     "time.windows.com",
88
89     // Google
90     "time1.google.com",
91     "time2.google.com",
92     "time3.google.com",
93     "time4.google.com",
94
95     // Russian Federation
96     "ntp.ix.ru",
97     "ntp1.stratum2.ru",
98     "ntp2.stratum2.ru",
99     "ntp3.stratum2.ru",
100     "ntp4.stratum2.ru",
101     "ntp5.stratum2.ru",
102     "ntp6.stratum2.ru",
103     "ntp7.stratum2.ru",
104     "ntp1.stratum1.ru",
105     "ntp2.stratum1.ru",
106     "ntp3.stratum1.ru",
107     "ntp4.stratum1.ru",
108     "ntp1.vniiftri.ru",
109     "ntp2.vniiftri.ru",
110     "ntp3.vniiftri.ru",
111     "ntp4.vniiftri.ru",
112     "ntp21.vniiftri.ru",
113     "ntp1.niiftri.irkutsk.ru",
114     "ntp2.niiftri.irkutsk.ru",
115     "vniiftri.khv.ru",
116     "vniiftri2.khv.ru",
117     "ntp0.zenon.net",
118     "ntp.mobatime.ru",
119     "0.ru.pool.ntp.org",
120     "1.ru.pool.ntp.org",
121     "2.ru.pool.ntp.org",
122     "3.ru.pool.ntp.org",
123
124     // United States
125     "ntp-01.caltech.edu",
126     "ntp-02.caltech.edu",
127     "ntp-03.caltech.edu",
128     "ntp-04.caltech.edu",
129     "nist1-pa.ustiming.org",
130     "time-a.nist.gov ",
131     "time-b.nist.gov ",
132     "time-c.nist.gov ",
133     "time-d.nist.gov ",
134     "nist1-macon.macon.ga.us",
135     "nist.netservicesgroup.com",
136     "nisttime.carsoncity.k12.mi.us",
137     "nist1-lnk.binary.net",
138     "wwv.nist.gov",
139     "time-a.timefreq.bldrdoc.gov",
140     "time-b.timefreq.bldrdoc.gov",
141     "time-c.timefreq.bldrdoc.gov",
142     "time.nist.gov",
143     "utcnist.colorado.edu",
144     "utcnist2.colorado.edu",
145     "ntp-nist.ldsbc.net",
146     "nist1-lv.ustiming.org",
147     "time-nw.nist.gov",
148     "nist-time-server.eoni.com",
149     "nist-time-server.eoni.com",
150     "ntp1.bu.edu",
151     "ntp2.bu.edu",
152     "ntp3.bu.edu",
153     "0.us.pool.ntp.org",
154     "1.us.pool.ntp.org",
155     "2.us.pool.ntp.org",
156     "3.us.pool.ntp.org",
157
158     // South Africa
159     "ntp1.meraka.csir.co.za",
160     "ntp.is.co.za",
161     "ntp2.is.co.za",
162     "igubu.saix.net",
163     "ntp1.neology.co.za",
164     "ntp2.neology.co.za",
165     "tick.meraka.csir.co.za",
166     "tock.meraka.csir.co.za",
167     "ntp.time.org.za",
168     "ntp1.meraka.csir.co.za",
169     "ntp2.meraka.csir.co.za",
170     "0.za.pool.ntp.org",
171     "1.za.pool.ntp.org",
172     "2.za.pool.ntp.org",
173     "3.za.pool.ntp.org",
174
175     // Italy
176     "ntp1.inrim.it",
177     "ntp2.inrim.it",
178     "0.it.pool.ntp.org",
179     "1.it.pool.ntp.org",
180     "2.it.pool.ntp.org",
181     "3.it.pool.ntp.org",
182
183     // Netherlands
184     "ntp0.nl.net",
185     "ntp1.nl.net",
186     "ntp2.nl.net",
187     "ntp.utwente.nl",
188     "0.nl.pool.ntp.org",
189     "1.nl.pool.ntp.org",
190     "2.nl.pool.ntp.org",
191     "3.nl.pool.ntp.org",
192
193     // United Kingdom
194     "ntp2d.mcc.ac.uk",
195     "ntp2c.mcc.ac.uk",
196     "ntp.exnet.com",
197     "ntp.cis.strath.ac.uk",
198     "ntppub.le.ac.uk",
199     "0.uk.pool.ntp.org",
200     "1.uk.pool.ntp.org",
201     "2.uk.pool.ntp.org",
202     "3.uk.pool.ntp.org",
203
204     // Japan
205     "ntp.nict.jp",
206     "0.jp.pool.ntp.org",
207     "1.jp.pool.ntp.org",
208     "2.jp.pool.ntp.org",
209     "3.jp.pool.ntp.org",
210
211     // ... To be continued
212 };
213
214 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
215     int nAttempt = 0;
216
217     while(nAttempt < 100) {
218         sockfd = -1;
219         nAttempt++;
220
221         int nServerNum = GetRandInt(nServersCount);
222
223         std::vector<CNetAddr> vIP;
224         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
225         if (!fRet)
226             continue;
227
228         struct sockaddr_in servaddr;
229         servaddr.sin_family = AF_INET;
230         servaddr.sin_port = htons(123);
231
232         bool found = false;
233         for(unsigned int i = 0; i < vIP.size(); i++) {
234             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
235                 break;
236             }
237         }
238
239         if (!found)
240             continue;
241
242         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
243
244         if (sockfd == INVALID_SOCKET)
245             continue; // socket initialization error
246
247         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
248             continue; // "connection" error
249         }
250
251         *pcliaddr = *((struct sockaddr *) &servaddr);
252         servlen = sizeof(servaddr);
253         return true;
254     }
255
256     return false;
257 }
258
259 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
260     sockfd = -1;
261
262     std::vector<CNetAddr> vIP;
263     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
264     if (!fRet) {
265         return false;
266     }
267
268     struct sockaddr_in servaddr;
269     servaddr.sin_family = AF_INET;
270     servaddr.sin_port = htons(123);
271
272     bool found = false;
273     for(unsigned int i = 0; i < vIP.size(); i++) {
274         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
275             break;
276         }
277     }
278
279     if (!found) {
280         return false;
281     }
282
283     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
284
285     if (sockfd == INVALID_SOCKET)
286         return false; // socket initialization error
287
288     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
289         return false; // "connection" error
290     }
291
292
293     *pcliaddr = *((struct sockaddr *) &servaddr);
294     servlen = sizeof(servaddr);
295
296     return true;
297 }
298
299
300 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
301 #ifdef WIN32
302     u_long nOne = 1;
303     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
304         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
305 #else
306     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
307         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
308 #endif
309         return -2;
310     }
311
312     struct pkt *msg = new pkt;
313     struct pkt *prt  = new pkt;
314
315     msg->li_vn_mode=227;
316     msg->stratum=0;
317     msg->ppoll=4;
318     msg->precision=0;
319     msg->rootdelay=0;
320     msg->rootdispersion=0;
321
322     msg->ref.Ul_i.Xl_i=0;
323     msg->ref.Ul_f.Xl_f=0;
324     msg->org.Ul_i.Xl_i=0;
325     msg->org.Ul_f.Xl_f=0;
326     msg->rec.Ul_i.Xl_i=0;
327     msg->rec.Ul_f.Xl_f=0;
328     msg->xmt.Ul_i.Xl_i=0;
329     msg->xmt.Ul_f.Xl_f=0;
330
331     int len=48;
332     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
333     if (retcode < 0) {
334         printf("sendto() failed: %d", retcode);
335         return -3;
336     }
337
338     fd_set fdset;
339     struct timeval timeout = {10, 0};
340     FD_ZERO(&fdset);
341     FD_SET(sockfd, &fdset);
342
343     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
344     if (retcode <= 0) {
345         printf("recvfrom() error");
346         return -4;
347     }
348
349     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
350
351     ntohl_fp(&msg->rec, &prt->rec);
352     ntohl_fp(&msg->xmt, &prt->xmt);
353
354     time_t seconds_receive;
355     time_t seconds_transmit;
356
357     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
358     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
359
360     delete msg;
361     delete prt;
362
363     return (seconds_receive + seconds_transmit) / 2;
364 }
365
366 int64_t NtpGetTime() {
367     struct sockaddr cliaddr;
368
369     SOCKET sockfd;
370     socklen_t servlen;
371
372     if (!InitWithRandom(sockfd, servlen, &cliaddr))
373         return -1;
374
375     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
376
377     closesocket(sockfd);
378
379     return nTime;
380 }
381
382 int64_t NtpGetTime(CNetAddr& ip) {
383     struct sockaddr cliaddr;
384
385     SOCKET sockfd;
386     socklen_t servlen;
387
388     if (!InitWithRandom(sockfd, servlen, &cliaddr))
389         return -1;
390
391     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
392     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
393
394     closesocket(sockfd);
395
396     return nTime;
397 }
398
399 int64_t NtpGetTime(std::string &strHostName)
400 {
401     struct sockaddr cliaddr;
402
403     SOCKET sockfd;
404     socklen_t servlen;
405
406     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
407         return -1;
408
409     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
410
411     closesocket(sockfd);
412
413     return nTime;
414 }
415
416 void ThreadNtpSamples(void* parg)
417 {
418     printf("ThreadNtpSamples started\n");
419     vnThreadsRunning[THREAD_NTP]++;
420
421     // Make this thread recognisable as time synchronization thread
422     RenameThread("novacoin-ntp-samples");
423
424     while (!fShutdown) {
425         CNetAddr ip;
426         int64_t nTime = NtpGetTime(ip);
427
428         if (nTime > 0 && nTime != 2085978496) { // Skip the deliberately wrong timestamps
429             AddTimeData(ip, nTime);
430         }
431         else {
432             Sleep(600000); // In case of failure wait 600 seconds and then try again
433             continue;
434         }
435
436         Sleep(43200000); // Sleep for 12 hours
437     }
438
439     vnThreadsRunning[THREAD_NTP]--;
440     printf("ThreadNtpSamples exited\n");
441 }