Add Hurricane Electric and SixXs servers.
[novacoin.git] / src / ntp.cpp
1
2 #ifdef WIN32
3 #include <winsock2.h>
4 #else
5 #include <sys/socket.h>
6 #include <sys/time.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <netdb.h>
10 #endif
11 #ifndef WIN32
12 #include <unistd.h>
13 #endif
14
15 #include "netbase.h"
16 #include "util.h"
17 #include "net.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 int nServersCount = 118;
84
85 std::string NtpServers[118] = {
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     "ntp-01.caltech.edu",
141     "ntp-02.caltech.edu",
142     "ntp-03.caltech.edu",
143     "ntp-04.caltech.edu",
144     "nist1-pa.ustiming.org",
145     "time-a.nist.gov ",
146     "time-b.nist.gov ",
147     "time-c.nist.gov ",
148     "time-d.nist.gov ",
149     "nist1-macon.macon.ga.us",
150     "nist.netservicesgroup.com",
151     "nisttime.carsoncity.k12.mi.us",
152     "nist1-lnk.binary.net",
153     "wwv.nist.gov",
154     "time-a.timefreq.bldrdoc.gov",
155     "time-b.timefreq.bldrdoc.gov",
156     "time-c.timefreq.bldrdoc.gov",
157     "time.nist.gov",
158     "utcnist.colorado.edu",
159     "utcnist2.colorado.edu",
160     "ntp-nist.ldsbc.net",
161     "nist1-lv.ustiming.org",
162     "time-nw.nist.gov",
163     "nist-time-server.eoni.com",
164     "nist-time-server.eoni.com",
165     "ntp1.bu.edu",
166     "ntp2.bu.edu",
167     "ntp3.bu.edu",
168     "0.us.pool.ntp.org",
169     "1.us.pool.ntp.org",
170     "2.us.pool.ntp.org",
171     "3.us.pool.ntp.org",
172
173     // South Africa
174     "ntp1.meraka.csir.co.za",
175     "ntp.is.co.za",
176     "ntp2.is.co.za",
177     "igubu.saix.net",
178     "ntp1.neology.co.za",
179     "ntp2.neology.co.za",
180     "tick.meraka.csir.co.za",
181     "tock.meraka.csir.co.za",
182     "ntp.time.org.za",
183     "ntp1.meraka.csir.co.za",
184     "ntp2.meraka.csir.co.za",
185     "0.za.pool.ntp.org",
186     "1.za.pool.ntp.org",
187     "2.za.pool.ntp.org",
188     "3.za.pool.ntp.org",
189
190     // Italy
191     "ntp1.inrim.it",
192     "ntp2.inrim.it",
193     "0.it.pool.ntp.org",
194     "1.it.pool.ntp.org",
195     "2.it.pool.ntp.org",
196     "3.it.pool.ntp.org",
197
198     // Netherlands
199     "ntp0.nl.net",
200     "ntp1.nl.net",
201     "ntp2.nl.net",
202     "ntp.utwente.nl",
203     "0.nl.pool.ntp.org",
204     "1.nl.pool.ntp.org",
205     "2.nl.pool.ntp.org",
206     "3.nl.pool.ntp.org",
207
208     // United Kingdom
209     "ntp2d.mcc.ac.uk",
210     "ntp2c.mcc.ac.uk",
211     "ntp.exnet.com",
212     "ntp.cis.strath.ac.uk",
213     "ntppub.le.ac.uk",
214     "0.uk.pool.ntp.org",
215     "1.uk.pool.ntp.org",
216     "2.uk.pool.ntp.org",
217     "3.uk.pool.ntp.org",
218
219     // Japan
220     "ntp.nict.jp",
221     "0.jp.pool.ntp.org",
222     "1.jp.pool.ntp.org",
223     "2.jp.pool.ntp.org",
224     "3.jp.pool.ntp.org",
225
226     // ... To be continued
227 };
228
229 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
230     int nAttempt = 0;
231
232     while(nAttempt < 100) {
233         sockfd = -1;
234         nAttempt++;
235
236         int nServerNum = GetRandInt(nServersCount);
237
238         std::vector<CNetAddr> vIP;
239         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
240         if (!fRet)
241             continue;
242
243         struct sockaddr_in servaddr;
244         servaddr.sin_family = AF_INET;
245         servaddr.sin_port = htons(123);
246
247         bool found = false;
248         for(unsigned int i = 0; i < vIP.size(); i++) {
249             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
250                 break;
251             }
252         }
253
254         if (!found)
255             continue;
256
257         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
258
259         if (sockfd == INVALID_SOCKET)
260             continue; // socket initialization error
261
262         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
263             continue; // "connection" error
264         }
265
266         *pcliaddr = *((struct sockaddr *) &servaddr);
267         servlen = sizeof(servaddr);
268         return true;
269     }
270
271     return false;
272 }
273
274 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
275     sockfd = -1;
276
277     std::vector<CNetAddr> vIP;
278     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
279     if (!fRet) {
280         return false;
281     }
282
283     struct sockaddr_in servaddr;
284     servaddr.sin_family = AF_INET;
285     servaddr.sin_port = htons(123);
286
287     bool found = false;
288     for(unsigned int i = 0; i < vIP.size(); i++) {
289         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
290             break;
291         }
292     }
293
294     if (!found) {
295         return false;
296     }
297
298     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
299
300     if (sockfd == INVALID_SOCKET)
301         return false; // socket initialization error
302
303     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
304         return false; // "connection" error
305     }
306
307
308     *pcliaddr = *((struct sockaddr *) &servaddr);
309     servlen = sizeof(servaddr);
310
311     return true;
312 }
313
314
315 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
316 #ifdef WIN32
317     u_long nOne = 1;
318     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
319         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
320 #else
321     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
322         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
323 #endif
324         return -2;
325     }
326
327     struct pkt *msg = new pkt;
328     struct pkt *prt  = new pkt;
329
330     msg->li_vn_mode=227;
331     msg->stratum=0;
332     msg->ppoll=4;
333     msg->precision=0;
334     msg->rootdelay=0;
335     msg->rootdispersion=0;
336
337     msg->ref.Ul_i.Xl_i=0;
338     msg->ref.Ul_f.Xl_f=0;
339     msg->org.Ul_i.Xl_i=0;
340     msg->org.Ul_f.Xl_f=0;
341     msg->rec.Ul_i.Xl_i=0;
342     msg->rec.Ul_f.Xl_f=0;
343     msg->xmt.Ul_i.Xl_i=0;
344     msg->xmt.Ul_f.Xl_f=0;
345
346     int len=48;
347     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
348     if (retcode < 0) {
349         printf("sendto() failed: %d", retcode);
350         return -3;
351     }
352
353     fd_set fdset;
354     struct timeval timeout = {10, 0};
355     FD_ZERO(&fdset);
356     FD_SET(sockfd, &fdset);
357
358     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
359     if (retcode <= 0) {
360         printf("recvfrom() error");
361         return -4;
362     }
363
364     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
365
366     ntohl_fp(&msg->rec, &prt->rec);
367     ntohl_fp(&msg->xmt, &prt->xmt);
368
369     time_t seconds_receive;
370     time_t seconds_transmit;
371
372     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
373     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
374
375     delete msg;
376     delete prt;
377
378     return (seconds_receive + seconds_transmit) / 2;
379 }
380
381 int64_t NtpGetTime() {
382     struct sockaddr cliaddr;
383
384     SOCKET sockfd;
385     socklen_t servlen;
386
387     if (!InitWithRandom(sockfd, servlen, &cliaddr))
388         return -1;
389
390     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
391
392     closesocket(sockfd);
393
394     return nTime;
395 }
396
397 int64_t NtpGetTime(CNetAddr& ip) {
398     struct sockaddr cliaddr;
399
400     SOCKET sockfd;
401     socklen_t servlen;
402
403     if (!InitWithRandom(sockfd, servlen, &cliaddr))
404         return -1;
405
406     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
407     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
408
409     closesocket(sockfd);
410
411     return nTime;
412 }
413
414 int64_t NtpGetTime(std::string &strHostName)
415 {
416     struct sockaddr cliaddr;
417
418     SOCKET sockfd;
419     socklen_t servlen;
420
421     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
422         return -1;
423
424     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
425
426     closesocket(sockfd);
427
428     return nTime;
429 }
430
431 void ThreadNtpSamples(void* parg)
432 {
433     printf("ThreadNtpSamples started\n");
434     vnThreadsRunning[THREAD_NTP]++;
435
436     // Make this thread recognisable as time synchronization thread
437     RenameThread("novacoin-ntp-samples");
438
439     while (!fShutdown) {
440         CNetAddr ip;
441         int64_t nTime = NtpGetTime(ip);
442
443         if (nTime > 0 && nTime != 2085978496) { // Skip the deliberately wrong timestamps
444             AddTimeData(ip, nTime);
445         }
446         else {
447             Sleep(600000); // In case of failure wait 600 seconds and then try again
448             continue;
449         }
450
451         Sleep(43200000); // Sleep for 12 hours
452     }
453
454     vnThreadsRunning[THREAD_NTP]--;
455     printf("ThreadNtpSamples exited\n");
456 }