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