More NTP servers.
[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 = 162;
84
85 std::string NtpServers[162] = {
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.nist.gov",
155     "time-a.nist.gov",
156     "time-b.nist.gov",
157     "time-c.nist.gov",
158     "time-d.nist.gov",
159     "time-nw.nist.gov",
160     "nist1-macon.macon.ga.us",
161     "nist.netservicesgroup.com",
162     "nisttime.carsoncity.k12.mi.us",
163     "nist1-lnk.binary.net",
164     "wwv.nist.gov",
165     "time-a.timefreq.bldrdoc.gov",
166     "time-b.timefreq.bldrdoc.gov",
167     "time-c.timefreq.bldrdoc.gov",
168     "utcnist.colorado.edu",
169     "utcnist2.colorado.edu",
170     "ntp-nist.ldsbc.net",
171     "nist1-lv.ustiming.org",
172     "time-nw.nist.gov",
173     "nist-time-server.eoni.com",
174     "nist-time-server.eoni.com",
175     "ntp1.bu.edu",
176     "ntp2.bu.edu",
177     "ntp3.bu.edu",
178     "0.us.pool.ntp.org",
179     "1.us.pool.ntp.org",
180     "2.us.pool.ntp.org",
181     "3.us.pool.ntp.org",
182     "wwv.otc.psu.edu",
183     "otc1.psu.edu",
184     "otc2.psu.edu",
185     "now.okstate.edu",
186     "ntp.colby.edu",
187     "bonehed.lcs.mit.edu",
188     "ntp-s1.cise.ufl.edu",
189
190     // South Africa
191     "ntp1.meraka.csir.co.za",
192     "ntp.is.co.za",
193     "ntp2.is.co.za",
194     "igubu.saix.net",
195     "ntp1.neology.co.za",
196     "ntp2.neology.co.za",
197     "tick.meraka.csir.co.za",
198     "tock.meraka.csir.co.za",
199     "ntp.time.org.za",
200     "ntp1.meraka.csir.co.za",
201     "ntp2.meraka.csir.co.za",
202     "0.za.pool.ntp.org",
203     "1.za.pool.ntp.org",
204     "2.za.pool.ntp.org",
205     "3.za.pool.ntp.org",
206
207     // Italy
208     "ntp0.ien.it",
209     "ntp1.ien.it",
210     "ntp2.ien.it",
211     "ntp1.inrim.it",
212     "ntp2.inrim.it",
213     "0.it.pool.ntp.org",
214     "1.it.pool.ntp.org",
215     "2.it.pool.ntp.org",
216     "3.it.pool.ntp.org",
217
218     // Netherlands
219     "ntp0.nl.net",
220     "ntp1.nl.net",
221     "ntp2.nl.net",
222     "ntp.utwente.nl",
223     "0.nl.pool.ntp.org",
224     "1.nl.pool.ntp.org",
225     "2.nl.pool.ntp.org",
226     "3.nl.pool.ntp.org",
227
228     // United Kingdom
229     "ntp2d.mcc.ac.uk",
230     "ntp2c.mcc.ac.uk",
231     "ntp2b.mcc.ac.uk",
232     "ntp.exnet.com",
233     "ntp.cis.strath.ac.uk",
234     "ntppub.le.ac.uk",
235     "0.uk.pool.ntp.org",
236     "1.uk.pool.ntp.org",
237     "2.uk.pool.ntp.org",
238     "3.uk.pool.ntp.org",
239
240     // Canada
241     "chime.utoronto.ca",
242     "tick.utoronto.ca",
243     "time.nrc.ca",
244     "timelord.uregina.ca",
245     "tock.utoronto.ca",
246     "www1.cmc.ec.gc.ca",
247     "www2.cmc.ec.gc.ca",
248     "0.ca.pool.ntp.org",
249     "1.ca.pool.ntp.org",
250     "2.ca.pool.ntp.org",
251     "3.ca.pool.ntp.org",
252
253     // Japan
254     "ntp.nict.jp",
255     "0.jp.pool.ntp.org",
256     "1.jp.pool.ntp.org",
257     "2.jp.pool.ntp.org",
258     "3.jp.pool.ntp.org",
259
260     // Australia
261     "ntp.cs.mu.oz.au",
262     "augean.eleceng.adelaide.edu.au",
263     "0.au.pool.ntp.org",
264     "1.au.pool.ntp.org",
265     "2.au.pool.ntp.org",
266     "3.au.pool.ntp.org",
267
268     // Slovenia
269     "time.ijs.si",
270
271     // Austria
272     "0.at.pool.ntp.org",
273     "1.at.pool.ntp.org",
274     "2.at.pool.ntp.org",
275     "3.at.pool.ntp.org",
276
277     // ???
278     "clepsydra.dec.com",
279
280     // ... To be continued
281 };
282
283 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
284     sockfd = -1;
285
286     std::vector<CNetAddr> vIP;
287     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
288     if (!fRet) {
289         return false;
290     }
291
292     struct sockaddr_in servaddr;
293     servaddr.sin_family = AF_INET;
294     servaddr.sin_port = htons(123);
295
296     bool found = false;
297     for(unsigned int i = 0; i < vIP.size(); i++) {
298         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
299             break;
300         }
301     }
302
303     if (!found) {
304         return false;
305     }
306
307     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
308
309     if (sockfd == INVALID_SOCKET)
310         return false; // socket initialization error
311
312     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
313         return false; // "connection" error
314     }
315
316
317     *pcliaddr = *((struct sockaddr *) &servaddr);
318     servlen = sizeof(servaddr);
319
320     return true;
321 }
322
323 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
324
325     for (int nAttempt = 0; nAttempt < nServersCount; nAttempt++) {
326         int nServerNum = GetRandInt(nServersCount);
327         if (InitWithHost(NtpServers[nServerNum], sockfd, servlen, pcliaddr)) {
328             return true;
329         }
330     }
331
332     return false;
333 }
334
335 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
336 #ifdef WIN32
337     u_long nOne = 1;
338     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
339         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
340 #else
341     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
342         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
343 #endif
344         return -2;
345     }
346
347     struct pkt *msg = new pkt;
348     struct pkt *prt  = new pkt;
349
350     msg->li_vn_mode=227;
351     msg->stratum=0;
352     msg->ppoll=4;
353     msg->precision=0;
354     msg->rootdelay=0;
355     msg->rootdispersion=0;
356
357     msg->ref.Ul_i.Xl_i=0;
358     msg->ref.Ul_f.Xl_f=0;
359     msg->org.Ul_i.Xl_i=0;
360     msg->org.Ul_f.Xl_f=0;
361     msg->rec.Ul_i.Xl_i=0;
362     msg->rec.Ul_f.Xl_f=0;
363     msg->xmt.Ul_i.Xl_i=0;
364     msg->xmt.Ul_f.Xl_f=0;
365
366     int len=48;
367     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
368     if (retcode < 0) {
369         printf("sendto() failed: %d\n", retcode);
370         return -3;
371     }
372
373     fd_set fdset;
374     struct timeval timeout = {10, 0};
375     FD_ZERO(&fdset);
376     FD_SET(sockfd, &fdset);
377
378     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
379     if (retcode <= 0) {
380         printf("recvfrom() error\n");
381         return -4;
382     }
383
384     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
385     ntohl_fp(&msg->xmt, &prt->xmt);
386     time_t seconds_transmit;
387     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
388
389     delete msg;
390     delete prt;
391
392     return seconds_transmit;
393 }
394
395 int64_t NtpGetTime(CNetAddr& ip) {
396     struct sockaddr cliaddr;
397
398     SOCKET sockfd;
399     socklen_t servlen;
400
401     if (!InitWithRandom(sockfd, servlen, &cliaddr))
402         return -1;
403
404     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
405     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
406
407     closesocket(sockfd);
408
409     return nTime;
410 }
411
412 int64_t NtpGetTime(std::string &strHostName)
413 {
414     struct sockaddr cliaddr;
415
416     SOCKET sockfd;
417     socklen_t servlen;
418
419     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
420         return -1;
421
422     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
423
424     closesocket(sockfd);
425
426     return nTime;
427 }
428
429 // NTP server, which we unconditionally trust. This may be your own installation of ntpd somewhere, for example. 
430 // "localhost" means "trust no one"
431 std::string strTrustedUpstream = "localhost";
432
433 // Current offset
434 int64_t nNtpOffset = INT64_MAX;
435
436 int64_t GetNtpOffset() {
437     return nNtpOffset;
438 }
439
440 void ThreadNtpSamples(void* parg) {
441     const int64_t nMaxOffset = 86400; // Not a real limit, just sanity threshold.
442
443     printf("Trying to find NTP server at localhost...\n");
444
445     std::string strLocalHost = "127.0.0.1";
446     if (NtpGetTime(strLocalHost) == GetTime()) {
447         printf("There is NTP server active at localhost,  we don't need NTP thread.\n");
448
449         nNtpOffset = 0;
450         return;
451     }
452
453     printf("ThreadNtpSamples started\n");
454     vnThreadsRunning[THREAD_NTP]++;
455
456     // Make this thread recognisable as time synchronization thread
457     RenameThread("novacoin-ntp-samples");
458
459     CMedianFilter<int64_t> vTimeOffsets(200,0);
460
461     while (!fShutdown) {
462         if (strTrustedUpstream != "localhost") {
463             // Trying to get new offset sample from trusted NTP server.
464             int64_t nClockOffset = NtpGetTime(strTrustedUpstream) - GetTime();
465
466             if (abs64(nClockOffset) < nMaxOffset) {
467                 // Everything seems right, remember new trusted offset.
468                 printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", strTrustedUpstream.c_str(), nClockOffset);
469                 nNtpOffset = nClockOffset;
470             }
471             else {
472                 // Something went wrong, disable trusted offset sampling.
473                 nNtpOffset = INT64_MAX;
474                 strTrustedUpstream = "localhost";
475
476                 int nSleepMinutes = 1 + GetRandInt(9); // Sleep for 1-10 minutes.
477                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++)
478                     Sleep(1000);
479
480                 continue;
481             }
482         }
483         else {
484             // Now, trying to get 2-4 samples from random NTP servers.
485             int nSamplesCount = 2 + GetRandInt(2);
486
487             for (int i = 0; i < nSamplesCount; i++) {
488                 CNetAddr ip;
489                 int64_t nClockOffset = NtpGetTime(ip) - GetTime();
490
491                 if (abs64(nClockOffset) < nMaxOffset) { // Skip the deliberately wrong timestamps
492                     printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", ip.ToString().c_str(), nClockOffset);
493                     vTimeOffsets.input(nClockOffset);
494                 }
495             }
496
497             if (vTimeOffsets.size() > 1) {
498                 nNtpOffset = vTimeOffsets.median();
499             }
500             else {
501                 // Not enough offsets yet, try to collect additional samples later.
502                 nNtpOffset = INT64_MAX;
503                 int nSleepMinutes = 1 + GetRandInt(4); // Sleep for 1-5 minutes.
504                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) 
505                     Sleep(1000);
506                 continue;
507             }
508         }
509
510         if (GetNodesOffset() == INT_MAX && abs64(nNtpOffset) > 40 * 60)
511         {
512             // If there is not enough node offsets data and NTP time offset is greater than 40 minutes then give a warning.
513             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.");
514             strMiscWarning = strMessage;
515             printf("*** %s\n", strMessage.c_str());
516             uiInterface.ThreadSafeMessageBox(strMessage+" ", std::string("NovaCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
517         }
518
519         printf("nNtpOffset = %+" PRId64 "  (%+" PRId64 " minutes)\n", nNtpOffset, nNtpOffset/60);
520
521         int nSleepHours = 1 + GetRandInt(5); // Sleep for 1-6 hours.
522         for (int i = 0; i < nSleepHours * 3600 && !fShutdown; i++)
523             Sleep(1000);
524     }
525
526     vnThreadsRunning[THREAD_NTP]--;
527     printf("ThreadNtpSamples exited\n");
528 }