Add CloseSocket (bitcoin@43f510d37d680ca4347878d2fb6f8b97b54e7611)
[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 = 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(const std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
284   
285     sockfd = INVALID_SOCKET;
286
287     std::vector<CNetAddr> vIP;
288     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
289     if (!fRet) {
290         return false;
291     }
292
293     struct sockaddr_in servaddr;
294     servaddr.sin_family = AF_INET;
295     servaddr.sin_port = htons(123);
296
297     bool found = false;
298     for(unsigned int i = 0; i < vIP.size(); i++) {
299         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)) != false) {
300             break;
301         }
302     }
303
304     if (!found) {
305         return false;
306     }
307
308     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
309
310     if (sockfd == INVALID_SOCKET)
311         return false; // socket initialization error
312
313     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
314         return false; // "connection" error
315     }
316
317
318     *pcliaddr = *((struct sockaddr *) &servaddr);
319     servlen = sizeof(servaddr);
320
321     return true;
322 }
323
324 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
325
326     for (int nAttempt = 0; nAttempt < nServersCount; nAttempt++) {
327         int nServerNum = GetRandInt(nServersCount);
328         if (InitWithHost(NtpServers[nServerNum], sockfd, servlen, pcliaddr)) {
329             return true;
330         }
331     }
332
333     return false;
334 }
335
336 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
337
338
339 #ifdef WIN32
340     u_long nOne = 1;
341     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
342         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
343 #else
344     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
345         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
346 #endif
347         return -2;
348     }
349
350     struct timeval timeout = {10, 0};
351     struct pkt *msg = new pkt;
352     struct pkt *prt  = new pkt;
353     time_t seconds_transmit;
354     int len = 48;
355
356     msg->li_vn_mode=227;
357     msg->stratum=0;
358     msg->ppoll=4;
359     msg->precision=0;
360     msg->rootdelay=0;
361     msg->rootdispersion=0;
362
363     msg->ref.Ul_i.Xl_i=0;
364     msg->ref.Ul_f.Xl_f=0;
365     msg->org.Ul_i.Xl_i=0;
366     msg->org.Ul_f.Xl_f=0;
367     msg->rec.Ul_i.Xl_i=0;
368     msg->rec.Ul_f.Xl_f=0;
369     msg->xmt.Ul_i.Xl_i=0;
370     msg->xmt.Ul_f.Xl_f=0;
371
372     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
373     if (retcode < 0) {
374         printf("sendto() failed: %d\n", retcode);
375         seconds_transmit = -3;
376         goto _end;
377     }
378
379     fd_set fdset;
380     FD_ZERO(&fdset);
381     FD_SET(sockfd, &fdset);
382
383     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
384     if (retcode <= 0) {
385         printf("recvfrom() error\n");
386         seconds_transmit = -4;
387         goto _end;
388     }
389
390     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
391     ntohl_fp(&msg->xmt, &prt->xmt);
392     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
393
394     _end:
395
396     delete msg;
397     delete prt;
398
399     return seconds_transmit;
400 }
401
402 int64_t NtpGetTime(CNetAddr& ip) {
403     struct sockaddr cliaddr;
404
405     SOCKET sockfd;
406     socklen_t servlen;
407
408     if (!InitWithRandom(sockfd, servlen, &cliaddr))
409         return -1;
410
411     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
412     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
413
414     CloseSocket(sockfd);
415
416     return nTime;
417 }
418
419 int64_t NtpGetTime(const std::string &strHostName)
420 {
421     struct sockaddr cliaddr;
422
423     SOCKET sockfd;
424     socklen_t servlen;
425
426     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
427         return -1;
428
429     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
430
431     CloseSocket(sockfd);
432
433     return nTime;
434 }
435
436 // NTP server, which we unconditionally trust. This may be your own installation of ntpd somewhere, for example. 
437 // "localhost" means "trust no one"
438 std::string strTrustedUpstream = "localhost";
439
440 // Current offset
441 int64_t nNtpOffset = INT64_MAX;
442
443 int64_t GetNtpOffset() {
444     return nNtpOffset;
445 }
446
447 void ThreadNtpSamples(void* parg) {
448     const int64_t nMaxOffset = 86400; // Not a real limit, just sanity threshold.
449
450     printf("Trying to find NTP server at localhost...\n");
451
452     std::string strLocalHost = "127.0.0.1";
453     if (NtpGetTime(strLocalHost) == GetTime()) {
454         printf("There is NTP server active at localhost,  we don't need NTP thread.\n");
455
456         nNtpOffset = 0;
457         return;
458     }
459
460     printf("ThreadNtpSamples started\n");
461     vnThreadsRunning[THREAD_NTP]++;
462
463     // Make this thread recognisable as time synchronization thread
464     RenameThread("novacoin-ntp-samples");
465
466     CMedianFilter<int64_t> vTimeOffsets(200,0);
467
468     while (!fShutdown) {
469         if (strTrustedUpstream != "localhost") {
470             // Trying to get new offset sample from trusted NTP server.
471             int64_t nClockOffset = NtpGetTime(strTrustedUpstream) - GetTime();
472
473             if (abs64(nClockOffset) < nMaxOffset) {
474                 // Everything seems right, remember new trusted offset.
475                 printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", strTrustedUpstream.c_str(), nClockOffset);
476                 nNtpOffset = nClockOffset;
477             }
478             else {
479                 // Something went wrong, disable trusted offset sampling.
480                 nNtpOffset = INT64_MAX;
481                 strTrustedUpstream = "localhost";
482
483                 int nSleepMinutes = 1 + GetRandInt(9); // Sleep for 1-10 minutes.
484                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++)
485                     Sleep(1000);
486
487                 continue;
488             }
489         }
490         else {
491             // Now, trying to get 2-4 samples from random NTP servers.
492             int nSamplesCount = 2 + GetRandInt(2);
493
494             for (int i = 0; i < nSamplesCount; i++) {
495                 CNetAddr ip;
496                 int64_t nClockOffset = NtpGetTime(ip) - GetTime();
497
498                 if (abs64(nClockOffset) < nMaxOffset) { // Skip the deliberately wrong timestamps
499                     printf("ThreadNtpSamples: new offset sample from %s, offset=%" PRId64 ".\n", ip.ToString().c_str(), nClockOffset);
500                     vTimeOffsets.input(nClockOffset);
501                 }
502             }
503
504             if (vTimeOffsets.size() > 1) {
505                 nNtpOffset = vTimeOffsets.median();
506             }
507             else {
508                 // Not enough offsets yet, try to collect additional samples later.
509                 nNtpOffset = INT64_MAX;
510                 int nSleepMinutes = 1 + GetRandInt(4); // Sleep for 1-5 minutes.
511                 for (int i = 0; i < nSleepMinutes * 60 && !fShutdown; i++) 
512                     Sleep(1000);
513                 continue;
514             }
515         }
516
517         if (GetNodesOffset() == INT_MAX && abs64(nNtpOffset) > 40 * 60)
518         {
519             // If there is not enough node offsets data and NTP time offset is greater than 40 minutes then give a warning.
520             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.");
521             strMiscWarning = strMessage;
522             printf("*** %s\n", strMessage.c_str());
523             uiInterface.ThreadSafeMessageBox(strMessage+" ", std::string("NovaCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
524         }
525
526         printf("nNtpOffset = %+" PRId64 "  (%+" PRId64 " minutes)\n", nNtpOffset, nNtpOffset/60);
527
528         int nSleepHours = 1 + GetRandInt(5); // Sleep for 1-6 hours.
529         for (int i = 0; i < nSleepHours * 3600 && !fShutdown; i++)
530             Sleep(1000);
531     }
532
533     vnThreadsRunning[THREAD_NTP]--;
534     printf("ThreadNtpSamples exited\n");
535 }