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