NTP: add initializers
[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=0;
36     int32_t Xl_i;
37   } Ul_i;
38   union {
39     uint32_t Xl_uf=0;
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=227;     /* leap indicator, version and mode */
58   uint8_t  stratum=0;        /* peer stratum */
59   uint8_t  ppoll=4;          /* peer poll interval */
60   int8_t  precision=0;      /* peer clock precision */
61   uint32_t    rootdelay=0;      /* distance to primary clock */
62   uint32_t    rootdispersion=0; /* clock dispersion */
63   uint32_t refid=0;          /* 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     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
347     if (retcode < 0) {
348         printf("sendto() failed: %d\n", retcode);
349         seconds_transmit = -3;
350         goto _end;
351     }
352
353     fd_set fdset;
354     FD_ZERO(&fdset);
355     FD_SET(sockfd, &fdset);
356
357     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
358     if (retcode <= 0) {
359         printf("recvfrom() error\n");
360         seconds_transmit = -4;
361         goto _end;
362     }
363
364     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
365     ntohl_fp(&msg->xmt, &prt->xmt);
366     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
367
368     _end:
369
370     delete msg;
371     delete prt;
372
373     return seconds_transmit;
374 }
375
376 int64_t NtpGetTime(CNetAddr& ip) {
377     struct sockaddr cliaddr;
378
379     SOCKET sockfd;
380     socklen_t servlen;
381
382     if (!InitWithRandom(sockfd, servlen, &cliaddr))
383         return -1;
384
385     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
386     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
387
388     CloseSocket(sockfd);
389
390     return nTime;
391 }
392
393 int64_t NtpGetTime(const std::string &strHostName)
394 {
395     struct sockaddr cliaddr;
396
397     SOCKET sockfd;
398     socklen_t servlen;
399
400     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
401         return -1;
402
403     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
404
405     CloseSocket(sockfd);
406
407     return nTime;
408 }
409
410 // NTP server, which we unconditionally trust. This may be your own installation of ntpd somewhere, for example. 
411 // "localhost" means "trust no one"
412 std::string strTrustedUpstream = "localhost";
413
414 // Current offset
415 int64_t nNtpOffset = numeric_limits<int64_t>::max();
416
417 int64_t GetNtpOffset() {
418     return nNtpOffset;
419 }
420
421 void ThreadNtpSamples(void* parg) {
422     const int64_t nMaxOffset = nOneDay; // Not a real limit, just sanity threshold.
423
424     printf("Trying to find NTP server at localhost...\n");
425
426     std::string strLocalHost = "127.0.0.1";
427     if (NtpGetTime(strLocalHost) == GetTime()) {
428         printf("There is NTP server active at localhost,  we don't need NTP thread.\n");
429
430         nNtpOffset = 0;
431         return;
432     }
433
434     printf("ThreadNtpSamples started\n");
435     vnThreadsRunning[THREAD_NTP]++;
436
437     // Make this thread recognisable as time synchronization thread
438     RenameThread("novacoin-ntp-samples");
439
440     CMedianFilter<int64_t> vTimeOffsets(200,0);
441
442     while (!fShutdown) {
443         if (strTrustedUpstream != "localhost") {
444             // Trying to get new offset sample from trusted NTP server.
445             int64_t nClockOffset = NtpGetTime(strTrustedUpstream) - GetTime();
446
447             if (abs(nClockOffset) < nMaxOffset) {
448                 // Everything seems right, remember new trusted offset.
449                 printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", strTrustedUpstream.c_str(), nClockOffset);
450                 nNtpOffset = nClockOffset;
451             }
452             else {
453                 // Something went wrong, disable trusted offset sampling.
454                 nNtpOffset = numeric_limits<int64_t>::max();
455                 strTrustedUpstream = "localhost";
456
457                 int nSleepMinutes = 1 + GetRandInt(9); // Sleep for 1-10 minutes.
458                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++)
459                     Sleep(1000);
460
461                 continue;
462             }
463         }
464         else {
465             // Now, trying to get 2-4 samples from random NTP servers.
466             int nSamplesCount = 2 + GetRandInt(2);
467
468             for (int i = 0; i < nSamplesCount; i++) {
469                 CNetAddr ip;
470                 int64_t nClockOffset = NtpGetTime(ip) - GetTime();
471
472                 if (abs(nClockOffset) < nMaxOffset) { // Skip the deliberately wrong timestamps
473                     printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", ip.ToString().c_str(), nClockOffset);
474                     vTimeOffsets.input(nClockOffset);
475                 }
476             }
477
478             if (vTimeOffsets.size() > 1) {
479                 nNtpOffset = vTimeOffsets.median();
480             }
481             else {
482                 // Not enough offsets yet, try to collect additional samples later.
483                 nNtpOffset = numeric_limits<int64_t>::max();
484                 int nSleepMinutes = 1 + GetRandInt(4); // Sleep for 1-5 minutes.
485                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) 
486                     Sleep(1000);
487                 continue;
488             }
489         }
490
491         if (GetNodesOffset() == numeric_limits<int64_t>::max() && abs(nNtpOffset) > 40 * 60)
492         {
493             // If there is not enough node offsets data and NTP time offset is greater than 40 minutes then give a warning.
494             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.");
495             strMiscWarning = strMessage;
496             printf("*** %s\n", strMessage.c_str());
497             uiInterface.ThreadSafeMessageBox(strMessage+" ", std::string("NovaCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
498         }
499
500         printf("nNtpOffset = %+" PRId64 "  (%+" PRId64 " minutes)\n", nNtpOffset, nNtpOffset/60);
501
502         int nSleepHours = 1 + GetRandInt(5); // Sleep for 1-6 hours.
503         for (int i = 0; i < nSleepHours * 3600 && !fShutdown; i++)
504             Sleep(1000);
505     }
506
507     vnThreadsRunning[THREAD_NTP]--;
508     printf("ThreadNtpSamples exited\n");
509 }