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