Remove abs64 inline
[novacoin.git] / src / ntp.cpp
1 #ifdef WIN32
2 #include <winsock2.h>
3 #else
4 #include <sys/socket.h>
5 #include <sys/time.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 #include <netdb.h>
9 #endif
10 #ifndef WIN32
11 #include <unistd.h>
12 #endif
13
14 #include "netbase.h"
15 #include "net.h"
16 #include "interface.h"
17
18 extern int GetRandInt(int nMax);
19
20 /*
21  * NTP uses two fixed point formats.  The first (l_fp) is the "long"
22  * format and is 64 bits long with the decimal between bits 31 and 32.
23  * This is used for time stamps in the NTP packet header (in network
24  * byte order) and for internal computations of offsets (in local host
25  * byte order). We use the same structure for both signed and unsigned
26  * values, which is a big hack but saves rewriting all the operators
27  * twice. Just to confuse this, we also sometimes just carry the
28  * fractional part in calculations, in both signed and unsigned forms.
29  * Anyway, an l_fp looks like:
30  *
31  *    0                   1                   2                   3
32  *    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
33  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34  *   |                         Integral Part                         |
35  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36  *   |                         Fractional Part                       |
37  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38  * REF http://www.eecis.udel.edu/~mills/database/rfc/rfc2030.txt
39  */
40
41
42 typedef struct {
43   union {
44     uint32_t Xl_ui;
45     int32_t Xl_i;
46   } Ul_i;
47   union {
48     uint32_t Xl_uf;
49     int32_t Xl_f;
50   } Ul_f;
51 } l_fp;
52
53
54 inline void Ntp2Unix(const uint32_t &n, time_t &u) {
55     // Ntp's time scale starts in 1900, Unix in 1970.
56
57     u = n - 0x83aa7e80; // 2208988800 1970 - 1900 in seconds
58 }
59
60 inline void ntohl_fp(l_fp *n, l_fp *h) {
61     (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui);
62     (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf);
63 }
64
65 struct pkt {
66   uint8_t  li_vn_mode;     /* leap indicator, version and mode */
67   uint8_t  stratum;        /* peer stratum */
68   uint8_t  ppoll;          /* peer poll interval */
69   int8_t  precision;      /* peer clock precision */
70   uint32_t    rootdelay;      /* distance to primary clock */
71   uint32_t    rootdispersion; /* clock dispersion */
72   uint32_t refid;          /* reference clock ID */
73   l_fp    ref;        /* time peer clock was last updated */
74   l_fp    org;            /* originate time stamp */
75   l_fp    rec;            /* receive time stamp */
76   l_fp    xmt;            /* transmit time stamp */
77
78   uint32_t exten[1];       /* misused */
79   uint8_t  mac[5 * sizeof(uint32_t)]; /* mac */
80 };
81
82 const int nServersCount = 154;
83
84 std::string NtpServers[nServersCount] = {
85     // Apple
86     "time.apple.com",
87
88     // Microsoft
89     "time.windows.com",
90
91     // Google
92     "time1.google.com",
93     "time2.google.com",
94     "time3.google.com",
95     "time4.google.com",
96
97     // Hurricane Electric
98     "clock.sjc.he.net",
99     "clock.nyc.he.net",
100
101     // Russian Federation
102     "ntp.karelia.pro",
103     "ntp.alpet.me",
104     "aviel.alpet.me",
105     "ntp.sampo.ru",
106     "ntp.szt.ru",
107     "ntp.ix.ru",
108     "ntp1.stratum2.ru",
109     "ntp2.stratum2.ru",
110     "ntp3.stratum2.ru",
111     "ntp4.stratum2.ru",
112     "ntp5.stratum2.ru",
113     "ntp1.stratum1.ru",
114     "ntp2.stratum1.ru",
115     "ntp3.stratum1.ru",
116     "ntp4.stratum1.ru",
117     "ntp5.stratum1.ru",
118     "ntp1.vniiftri.ru",
119     "ntp2.vniiftri.ru",
120     "ntp3.vniiftri.ru",
121     "ntp4.vniiftri.ru",
122     "ntp21.vniiftri.ru",
123     "ntp1.niiftri.irkutsk.ru",
124     "ntp2.niiftri.irkutsk.ru",
125     "vniiftri.khv.ru",
126     "vniiftri2.khv.ru",
127     "ntp0.zenon.net",
128     "ntp.mobatime.ru",
129     "0.ru.pool.ntp.org",
130     "1.ru.pool.ntp.org",
131     "2.ru.pool.ntp.org",
132     "3.ru.pool.ntp.org",
133
134     // United States
135     "tock.cs.unlv.edu",
136     "timex.cs.columbia.edu",
137     "tick.cs.unlv.edu",
138     "sundial.columbia.edu",
139     "ntp-1.ece.cmu.edu",
140     "ntp-2.ece.cmu.edu",
141     "ntp-3.ece.cmu.edu",
142     "ntp1.cs.wisc.edu",
143     "ntp2.cs.wisc.edu",
144     "ntp3.cs.wisc.edu",
145     "ntp4.cs.wisc.edu",
146     "ntp-01.caltech.edu",
147     "ntp-02.caltech.edu",
148     "ntp-03.caltech.edu",
149     "ntp-04.caltech.edu",
150     "nist0-pa.ustiming.org",
151     "nist1-pa.ustiming.org",
152     "nist2-pa.ustiming.org",
153     "time.nist.gov",
154     "time-a.nist.gov",
155     "time-b.nist.gov",
156     "time-c.nist.gov",
157     "time-d.nist.gov",
158     "time-nw.nist.gov",
159     "nist1-macon.macon.ga.us",
160     "nist.netservicesgroup.com",
161     "wwv.nist.gov",
162     "time-a.timefreq.bldrdoc.gov",
163     "time-b.timefreq.bldrdoc.gov",
164     "time-c.timefreq.bldrdoc.gov",
165     "utcnist.colorado.edu",
166     "utcnist2.colorado.edu",
167     "nist1-lv.ustiming.org",
168     "time-nw.nist.gov",
169     "ntp1.bu.edu",
170     "ntp2.bu.edu",
171     "ntp3.bu.edu",
172     "0.us.pool.ntp.org",
173     "1.us.pool.ntp.org",
174     "2.us.pool.ntp.org",
175     "3.us.pool.ntp.org",
176     "otc1.psu.edu",
177     "otc2.psu.edu",
178     "now.okstate.edu",
179     "ntp.colby.edu",
180     "bonehed.lcs.mit.edu",
181     "ntp-s1.cise.ufl.edu",
182
183     // South Africa
184     "ntp1.meraka.csir.co.za",
185     "ntp.is.co.za",
186     "ntp2.is.co.za",
187     "igubu.saix.net",
188     "ntp1.neology.co.za",
189     "ntp2.neology.co.za",
190     "tick.meraka.csir.co.za",
191     "tock.meraka.csir.co.za",
192     "ntp.time.org.za",
193     "ntp1.meraka.csir.co.za",
194     "ntp2.meraka.csir.co.za",
195     "0.za.pool.ntp.org",
196     "1.za.pool.ntp.org",
197     "2.za.pool.ntp.org",
198     "3.za.pool.ntp.org",
199
200     // Italy
201     "ntp0.ien.it",
202     "ntp1.ien.it",
203     "ntp2.ien.it",
204     "ntp1.inrim.it",
205     "ntp2.inrim.it",
206     "0.it.pool.ntp.org",
207     "1.it.pool.ntp.org",
208     "2.it.pool.ntp.org",
209     "3.it.pool.ntp.org",
210
211     // Netherlands
212     "ntp0.nl.net",
213     "ntp1.nl.net",
214     "ntp2.nl.net",
215     "ntp.utwente.nl",
216     "0.nl.pool.ntp.org",
217     "1.nl.pool.ntp.org",
218     "2.nl.pool.ntp.org",
219     "3.nl.pool.ntp.org",
220
221     // United Kingdom
222     "ntp2d.mcc.ac.uk",
223     "ntp2c.mcc.ac.uk",
224     "ntp2b.mcc.ac.uk",
225     "ntp.exnet.com",
226     "ntp.cis.strath.ac.uk",
227     "ntppub.le.ac.uk",
228     "0.uk.pool.ntp.org",
229     "1.uk.pool.ntp.org",
230     "2.uk.pool.ntp.org",
231     "3.uk.pool.ntp.org",
232
233     // Canada
234     "chime.utoronto.ca",
235     "tick.utoronto.ca",
236     "time.nrc.ca",
237     "timelord.uregina.ca",
238     "tock.utoronto.ca",
239     "0.ca.pool.ntp.org",
240     "1.ca.pool.ntp.org",
241     "2.ca.pool.ntp.org",
242     "3.ca.pool.ntp.org",
243
244     // Japan
245     "ntp.nict.jp",
246     "0.jp.pool.ntp.org",
247     "1.jp.pool.ntp.org",
248     "2.jp.pool.ntp.org",
249     "3.jp.pool.ntp.org",
250
251     // Australia
252     "ntp.cs.mu.oz.au",
253     "augean.eleceng.adelaide.edu.au",
254     "0.au.pool.ntp.org",
255     "1.au.pool.ntp.org",
256     "2.au.pool.ntp.org",
257     "3.au.pool.ntp.org",
258
259     // Slovenia
260     "time.ijs.si",
261
262     // Austria
263     "0.at.pool.ntp.org",
264     "1.at.pool.ntp.org",
265     "2.at.pool.ntp.org",
266     "3.at.pool.ntp.org",
267
268     // ???
269     "clepsydra.dec.com",
270
271     // ... To be continued
272 };
273
274 bool InitWithHost(const std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
275   
276     sockfd = INVALID_SOCKET;
277
278     std::vector<CNetAddr> vIP;
279     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
280     if (!fRet) {
281         return false;
282     }
283
284     struct sockaddr_in servaddr;
285     servaddr.sin_family = AF_INET;
286     servaddr.sin_port = htons(123);
287
288     bool found = false;
289     for(unsigned int i = 0; i < vIP.size(); i++) {
290         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)) != false) {
291             break;
292         }
293     }
294
295     if (!found) {
296         return false;
297     }
298
299     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
300
301     if (sockfd == INVALID_SOCKET)
302         return false; // socket initialization error
303
304     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
305         return false; // "connection" error
306     }
307
308
309     *pcliaddr = *((struct sockaddr *) &servaddr);
310     servlen = sizeof(servaddr);
311
312     return true;
313 }
314
315 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
316
317     for (int nAttempt = 0; nAttempt < nServersCount; nAttempt++) {
318         int nServerNum = GetRandInt(nServersCount);
319         if (InitWithHost(NtpServers[nServerNum], sockfd, servlen, pcliaddr)) {
320             return true;
321         }
322     }
323
324     return false;
325 }
326
327 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
328
329
330 #ifdef WIN32
331     u_long nOne = 1;
332     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
333         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
334 #else
335     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
336         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
337 #endif
338         return -2;
339     }
340
341     struct timeval timeout = {10, 0};
342     struct pkt *msg = new pkt;
343     struct pkt *prt  = new pkt;
344     time_t seconds_transmit;
345     int len = 48;
346
347     msg->li_vn_mode=227;
348     msg->stratum=0;
349     msg->ppoll=4;
350     msg->precision=0;
351     msg->rootdelay=0;
352     msg->rootdispersion=0;
353
354     msg->ref.Ul_i.Xl_i=0;
355     msg->ref.Ul_f.Xl_f=0;
356     msg->org.Ul_i.Xl_i=0;
357     msg->org.Ul_f.Xl_f=0;
358     msg->rec.Ul_i.Xl_i=0;
359     msg->rec.Ul_f.Xl_f=0;
360     msg->xmt.Ul_i.Xl_i=0;
361     msg->xmt.Ul_f.Xl_f=0;
362
363     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
364     if (retcode < 0) {
365         printf("sendto() failed: %d\n", retcode);
366         seconds_transmit = -3;
367         goto _end;
368     }
369
370     fd_set fdset;
371     FD_ZERO(&fdset);
372     FD_SET(sockfd, &fdset);
373
374     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
375     if (retcode <= 0) {
376         printf("recvfrom() error\n");
377         seconds_transmit = -4;
378         goto _end;
379     }
380
381     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
382     ntohl_fp(&msg->xmt, &prt->xmt);
383     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
384
385     _end:
386
387     delete msg;
388     delete prt;
389
390     return seconds_transmit;
391 }
392
393 int64_t NtpGetTime(CNetAddr& ip) {
394     struct sockaddr cliaddr;
395
396     SOCKET sockfd;
397     socklen_t servlen;
398
399     if (!InitWithRandom(sockfd, servlen, &cliaddr))
400         return -1;
401
402     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
403     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
404
405     CloseSocket(sockfd);
406
407     return nTime;
408 }
409
410 int64_t NtpGetTime(const std::string &strHostName)
411 {
412     struct sockaddr cliaddr;
413
414     SOCKET sockfd;
415     socklen_t servlen;
416
417     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
418         return -1;
419
420     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
421
422     CloseSocket(sockfd);
423
424     return nTime;
425 }
426
427 // NTP server, which we unconditionally trust. This may be your own installation of ntpd somewhere, for example. 
428 // "localhost" means "trust no one"
429 std::string strTrustedUpstream = "localhost";
430
431 // Current offset
432 int64_t nNtpOffset = INT64_MAX;
433
434 int64_t GetNtpOffset() {
435     return nNtpOffset;
436 }
437
438 void ThreadNtpSamples(void* parg) {
439     const int64_t nMaxOffset = nOneDay; // Not a real limit, just sanity threshold.
440
441     printf("Trying to find NTP server at localhost...\n");
442
443     std::string strLocalHost = "127.0.0.1";
444     if (NtpGetTime(strLocalHost) == GetTime()) {
445         printf("There is NTP server active at localhost,  we don't need NTP thread.\n");
446
447         nNtpOffset = 0;
448         return;
449     }
450
451     printf("ThreadNtpSamples started\n");
452     vnThreadsRunning[THREAD_NTP]++;
453
454     // Make this thread recognisable as time synchronization thread
455     RenameThread("novacoin-ntp-samples");
456
457     CMedianFilter<int64_t> vTimeOffsets(200,0);
458
459     while (!fShutdown) {
460         if (strTrustedUpstream != "localhost") {
461             // Trying to get new offset sample from trusted NTP server.
462             int64_t nClockOffset = NtpGetTime(strTrustedUpstream) - GetTime();
463
464             if (abs(nClockOffset) < nMaxOffset) {
465                 // Everything seems right, remember new trusted offset.
466                 printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", strTrustedUpstream.c_str(), nClockOffset);
467                 nNtpOffset = nClockOffset;
468             }
469             else {
470                 // Something went wrong, disable trusted offset sampling.
471                 nNtpOffset = INT64_MAX;
472                 strTrustedUpstream = "localhost";
473
474                 int nSleepMinutes = 1 + GetRandInt(9); // Sleep for 1-10 minutes.
475                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++)
476                     Sleep(1000);
477
478                 continue;
479             }
480         }
481         else {
482             // Now, trying to get 2-4 samples from random NTP servers.
483             int nSamplesCount = 2 + GetRandInt(2);
484
485             for (int i = 0; i < nSamplesCount; i++) {
486                 CNetAddr ip;
487                 int64_t nClockOffset = NtpGetTime(ip) - GetTime();
488
489                 if (abs(nClockOffset) < nMaxOffset) { // Skip the deliberately wrong timestamps
490                     printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", ip.ToString().c_str(), nClockOffset);
491                     vTimeOffsets.input(nClockOffset);
492                 }
493             }
494
495             if (vTimeOffsets.size() > 1) {
496                 nNtpOffset = vTimeOffsets.median();
497             }
498             else {
499                 // Not enough offsets yet, try to collect additional samples later.
500                 nNtpOffset = INT64_MAX;
501                 int nSleepMinutes = 1 + GetRandInt(4); // Sleep for 1-5 minutes.
502                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) 
503                     Sleep(1000);
504                 continue;
505             }
506         }
507
508         if (GetNodesOffset() == INT_MAX && abs(nNtpOffset) > 40 * 60)
509         {
510             // If there is not enough node offsets data and NTP time offset is greater than 40 minutes then give a warning.
511             std::string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong NovaCoin will not work properly.");
512             strMiscWarning = strMessage;
513             printf("*** %s\n", strMessage.c_str());
514             uiInterface.ThreadSafeMessageBox(strMessage+" ", std::string("NovaCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
515         }
516
517         printf("nNtpOffset = %+" PRId64 "  (%+" PRId64 " minutes)\n", nNtpOffset, nNtpOffset/60);
518
519         int nSleepHours = 1 + GetRandInt(5); // Sleep for 1-6 hours.
520         for (int i = 0; i < nSleepHours * 3600 && !fShutdown; i++)
521             Sleep(1000);
522     }
523
524     vnThreadsRunning[THREAD_NTP]--;
525     printf("ThreadNtpSamples exited\n");
526 }