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