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