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