Keep old GetAdjustedTime() behavior if no NTP data is available;
[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 "util.h"
17 #include "ui_interface.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 const int nServersCount = 147;
84
85 std::string NtpServers[147] = {
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     // Hurricane Electric
96     "clock.sjc.he.net",
97     "clock.nyc.he.net",
98
99     // SixXS
100     "ntp.sixxs.net",
101     "ntp.eu.sixxs.net",
102     "ntp.us.sixxs.net",
103     "ntp.ap.sixxs.net",
104
105     // Russian Federation
106     "ntp.karelia.pro",
107     "ntp.alpet.me",
108     "aviel.alpet.me",
109     "ntp.sampo.ru",
110     "ntp.szt.ru",
111     "ntp.ix.ru",
112     "ntp1.stratum2.ru",
113     "ntp2.stratum2.ru",
114     "ntp3.stratum2.ru",
115     "ntp4.stratum2.ru",
116     "ntp5.stratum2.ru",
117     "ntp6.stratum2.ru",
118     "ntp7.stratum2.ru",
119     "ntp1.stratum1.ru",
120     "ntp2.stratum1.ru",
121     "ntp3.stratum1.ru",
122     "ntp4.stratum1.ru",
123     "ntp1.vniiftri.ru",
124     "ntp2.vniiftri.ru",
125     "ntp3.vniiftri.ru",
126     "ntp4.vniiftri.ru",
127     "ntp21.vniiftri.ru",
128     "ntp1.niiftri.irkutsk.ru",
129     "ntp2.niiftri.irkutsk.ru",
130     "vniiftri.khv.ru",
131     "vniiftri2.khv.ru",
132     "ntp0.zenon.net",
133     "ntp.mobatime.ru",
134     "0.ru.pool.ntp.org",
135     "1.ru.pool.ntp.org",
136     "2.ru.pool.ntp.org",
137     "3.ru.pool.ntp.org",
138
139     // United States
140     "tock.cs.unlv.edu",
141     "timex.cs.columbia.edu",
142     "tick.cs.unlv.edu",
143     "sundial.columbia.edu",
144     "ntp-1.ece.cmu.edu",
145     "ntp-2.ece.cmu.edu",
146     "ntp1.cs.wisc.edu",
147     "ntp2.cs.wisc.edu",
148     "ntp3.cs.wisc.edu",
149     "ntp-01.caltech.edu",
150     "ntp-02.caltech.edu",
151     "ntp-03.caltech.edu",
152     "ntp-04.caltech.edu",
153     "nist1-pa.ustiming.org",
154     "time-a.nist.gov ",
155     "time-b.nist.gov ",
156     "time-c.nist.gov ",
157     "time-d.nist.gov ",
158     "nist1-macon.macon.ga.us",
159     "nist.netservicesgroup.com",
160     "nisttime.carsoncity.k12.mi.us",
161     "nist1-lnk.binary.net",
162     "wwv.nist.gov",
163     "time-a.timefreq.bldrdoc.gov",
164     "time-b.timefreq.bldrdoc.gov",
165     "time-c.timefreq.bldrdoc.gov",
166     "time.nist.gov",
167     "utcnist.colorado.edu",
168     "utcnist2.colorado.edu",
169     "ntp-nist.ldsbc.net",
170     "nist1-lv.ustiming.org",
171     "time-nw.nist.gov",
172     "nist-time-server.eoni.com",
173     "nist-time-server.eoni.com",
174     "ntp1.bu.edu",
175     "ntp2.bu.edu",
176     "ntp3.bu.edu",
177     "0.us.pool.ntp.org",
178     "1.us.pool.ntp.org",
179     "2.us.pool.ntp.org",
180     "3.us.pool.ntp.org",
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     "ntp1.inrim.it",
201     "ntp2.inrim.it",
202     "0.it.pool.ntp.org",
203     "1.it.pool.ntp.org",
204     "2.it.pool.ntp.org",
205     "3.it.pool.ntp.org",
206
207     // Netherlands
208     "ntp0.nl.net",
209     "ntp1.nl.net",
210     "ntp2.nl.net",
211     "ntp.utwente.nl",
212     "0.nl.pool.ntp.org",
213     "1.nl.pool.ntp.org",
214     "2.nl.pool.ntp.org",
215     "3.nl.pool.ntp.org",
216
217     // United Kingdom
218     "ntp2d.mcc.ac.uk",
219     "ntp2c.mcc.ac.uk",
220     "ntp2b.mcc.ac.uk",
221     "ntp.exnet.com",
222     "ntp.cis.strath.ac.uk",
223     "ntppub.le.ac.uk",
224     "0.uk.pool.ntp.org",
225     "1.uk.pool.ntp.org",
226     "2.uk.pool.ntp.org",
227     "3.uk.pool.ntp.org",
228
229     // Canada
230     "chime.utoronto.ca",
231     "tick.utoronto.ca",
232     "time.nrc.ca",
233     "timelord.uregina.ca",
234     "tock.utoronto.ca",
235     "www1.cmc.ec.gc.ca",
236     "www2.cmc.ec.gc.ca",
237     "0.ca.pool.ntp.org",
238     "1.ca.pool.ntp.org",
239     "2.ca.pool.ntp.org",
240     "3.ca.pool.ntp.org",
241
242     // Japan
243     "ntp.nict.jp",
244     "0.jp.pool.ntp.org",
245     "1.jp.pool.ntp.org",
246     "2.jp.pool.ntp.org",
247     "3.jp.pool.ntp.org",
248
249     // Australia
250     "ntp.cs.mu.oz.au",
251     "augean.eleceng.adelaide.edu.au",
252     "0.au.pool.ntp.org",
253     "1.au.pool.ntp.org",
254     "2.au.pool.ntp.org",
255     "3.au.pool.ntp.org",
256
257     // Slovenia
258     "time.ijs.si",
259
260     // ???
261     "clepsydra.dec.com",
262
263     // ... To be continued
264 };
265
266 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
267     sockfd = -1;
268
269     std::vector<CNetAddr> vIP;
270     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
271     if (!fRet) {
272         return false;
273     }
274
275     struct sockaddr_in servaddr;
276     servaddr.sin_family = AF_INET;
277     servaddr.sin_port = htons(123);
278
279     bool found = false;
280     for(unsigned int i = 0; i < vIP.size(); i++) {
281         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
282             break;
283         }
284     }
285
286     if (!found) {
287         return false;
288     }
289
290     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
291
292     if (sockfd == INVALID_SOCKET)
293         return false; // socket initialization error
294
295     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
296         return false; // "connection" error
297     }
298
299
300     *pcliaddr = *((struct sockaddr *) &servaddr);
301     servlen = sizeof(servaddr);
302
303     return true;
304 }
305
306 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
307
308     for (int nAttempt = 0; nAttempt < nServersCount; nAttempt++) {
309         int nServerNum = GetRandInt(nServersCount);
310         if (InitWithHost(NtpServers[nServerNum], sockfd, servlen, pcliaddr)) {
311             return true;
312         }
313     }
314
315     return false;
316 }
317
318 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
319 #ifdef WIN32
320     u_long nOne = 1;
321     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
322         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
323 #else
324     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
325         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
326 #endif
327         return -2;
328     }
329
330     struct pkt *msg = new pkt;
331     struct pkt *prt  = new pkt;
332
333     msg->li_vn_mode=227;
334     msg->stratum=0;
335     msg->ppoll=4;
336     msg->precision=0;
337     msg->rootdelay=0;
338     msg->rootdispersion=0;
339
340     msg->ref.Ul_i.Xl_i=0;
341     msg->ref.Ul_f.Xl_f=0;
342     msg->org.Ul_i.Xl_i=0;
343     msg->org.Ul_f.Xl_f=0;
344     msg->rec.Ul_i.Xl_i=0;
345     msg->rec.Ul_f.Xl_f=0;
346     msg->xmt.Ul_i.Xl_i=0;
347     msg->xmt.Ul_f.Xl_f=0;
348
349     int len=48;
350     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
351     if (retcode < 0) {
352         printf("sendto() failed: %d\n", retcode);
353         return -3;
354     }
355
356     fd_set fdset;
357     struct timeval timeout = {10, 0};
358     FD_ZERO(&fdset);
359     FD_SET(sockfd, &fdset);
360
361     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
362     if (retcode <= 0) {
363         printf("recvfrom() error\n");
364         return -4;
365     }
366
367     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
368     ntohl_fp(&msg->xmt, &prt->xmt);
369     time_t seconds_transmit;
370     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
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(std::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 std::string strTrustedUpstream = "localhost";
415
416 // Current offset
417 int64_t nNtpOffset = INT64_MAX;
418
419 int64_t GetNtpOffset() {
420     return nNtpOffset;
421 }
422
423 void ThreadNtpSamples(void* parg) {
424     const int64_t nMaxOffset = 86400; // Not a real limit, just sanity threshold.
425
426     printf("Trying to find NTP server at localhost...\n");
427
428     std::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 (abs64(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 = INT64_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 (abs64(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 = INT64_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() == INT_MAX && abs64(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             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.");
497             strMiscWarning = strMessage;
498             printf("*** %s\n", strMessage.c_str());
499             uiInterface.ThreadSafeMessageBox(strMessage+" ", std::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 }