EOL
[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 const 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(const 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\n", 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\n");
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(const 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     const 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 != strLocalHost) {
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                 printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", strTrustedUpstream.c_str(), nClockOffset);
453                 nNtpOffset = nClockOffset;
454             }
455             else {
456                 // Something went wrong, disable trusted offset sampling.
457                 nNtpOffset = INT64_MAX;
458                 strTrustedUpstream = strLocalHost;
459
460                 int nSleepMinutes = 1 + GetRandInt(9); // Sleep for 1-10 minutes.
461                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++)
462                     Sleep(1000);
463
464                 continue;
465             }
466         }
467         else {
468             // Now, trying to get 2-4 samples from random NTP servers.
469             int nSamplesCount = 2 + GetRandInt(2);
470
471             for (int i = 0; i < nSamplesCount; i++) {
472                 CNetAddr ip;
473                 int64_t nClockOffset = NtpGetTime(ip) - GetTime();
474
475                 if (abs64(nClockOffset) < nMaxOffset) { // Skip the deliberately wrong timestamps
476                     printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", ip.ToString().c_str(), nClockOffset);
477                     vTimeOffsets.input(nClockOffset);
478                 }
479             }
480
481             if (vTimeOffsets.size() > 2) {
482                 nNtpOffset = vTimeOffsets.median();
483             }
484             else {
485                 // Not enough offsets yet, try again later.
486                 nNtpOffset = INT64_MAX;
487                 int nSleepMinutes = 1 + GetRandInt(4); // Sleep for 1-5 minutes.
488                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) 
489                     Sleep(1000);
490                 continue;
491             }
492         }
493
494         printf("nNtpOffset = %+" PRId64 "  (%+" PRId64 " minutes)\n", nNtpOffset, nNtpOffset/60);
495
496         int nSleepHours = 1 + GetRandInt(5); // Sleep for 1-6 hours.
497         for (int i = 0; i < nSleepHours * 3600 && !fShutdown; i++)
498             Sleep(1000);
499     }
500
501     vnThreadsRunning[THREAD_NTP]--;
502     printf("ThreadNtpSamples exited\n");
503 }