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