b68110e72a4b761f0c7a875d59b275504adb6a1e
[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 InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
230     sockfd = -1;
231
232     std::vector<CNetAddr> vIP;
233     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
234     if (!fRet) {
235         return false;
236     }
237
238     struct sockaddr_in servaddr;
239     servaddr.sin_family = AF_INET;
240     servaddr.sin_port = htons(123);
241
242     bool found = false;
243     for(unsigned int i = 0; i < vIP.size(); i++) {
244         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) {
245             break;
246         }
247     }
248
249     if (!found) {
250         return false;
251     }
252
253     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
254
255     if (sockfd == INVALID_SOCKET)
256         return false; // socket initialization error
257
258     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) {
259         return false; // "connection" error
260     }
261
262
263     *pcliaddr = *((struct sockaddr *) &servaddr);
264     servlen = sizeof(servaddr);
265
266     return true;
267 }
268
269 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) {
270
271     for (int nAttempt = 0; nAttempt < nServersCount; nAttempt++) {
272         int nServerNum = GetRandInt(nServersCount);
273         if (InitWithHost(NtpServers[nServerNum], sockfd, servlen, pcliaddr)) {
274             return true;
275         }
276     }
277
278     return false;
279 }
280
281 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr) {
282 #ifdef WIN32
283     u_long nOne = 1;
284     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR) {
285         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
286 #else
287     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) {
288         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
289 #endif
290         return -2;
291     }
292
293     struct pkt *msg = new pkt;
294     struct pkt *prt  = new pkt;
295
296     msg->li_vn_mode=227;
297     msg->stratum=0;
298     msg->ppoll=4;
299     msg->precision=0;
300     msg->rootdelay=0;
301     msg->rootdispersion=0;
302
303     msg->ref.Ul_i.Xl_i=0;
304     msg->ref.Ul_f.Xl_f=0;
305     msg->org.Ul_i.Xl_i=0;
306     msg->org.Ul_f.Xl_f=0;
307     msg->rec.Ul_i.Xl_i=0;
308     msg->rec.Ul_f.Xl_f=0;
309     msg->xmt.Ul_i.Xl_i=0;
310     msg->xmt.Ul_f.Xl_f=0;
311
312     int len=48;
313     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
314     if (retcode < 0) {
315         printf("sendto() failed: %d", retcode);
316         return -3;
317     }
318
319     fd_set fdset;
320     struct timeval timeout = {10, 0};
321     FD_ZERO(&fdset);
322     FD_SET(sockfd, &fdset);
323
324     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
325     if (retcode <= 0) {
326         printf("recvfrom() error");
327         return -4;
328     }
329
330     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
331
332     ntohl_fp(&msg->rec, &prt->rec);
333     ntohl_fp(&msg->xmt, &prt->xmt);
334
335     time_t seconds_receive;
336     time_t seconds_transmit;
337
338     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
339     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
340
341     delete msg;
342     delete prt;
343
344     return (seconds_receive + seconds_transmit) / 2;
345 }
346
347 int64_t NtpGetTime(CNetAddr& ip) {
348     struct sockaddr cliaddr;
349
350     SOCKET sockfd;
351     socklen_t servlen;
352
353     if (!InitWithRandom(sockfd, servlen, &cliaddr))
354         return -1;
355
356     ip = CNetAddr(((sockaddr_in *)&cliaddr)->sin_addr);
357     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
358
359     closesocket(sockfd);
360
361     return nTime;
362 }
363
364 int64_t NtpGetTime(std::string &strHostName)
365 {
366     struct sockaddr cliaddr;
367
368     SOCKET sockfd;
369     socklen_t servlen;
370
371     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
372         return -1;
373
374     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
375
376     closesocket(sockfd);
377
378     return nTime;
379 }
380
381 void ThreadNtpSamples(void* parg)
382 {
383     printf("ThreadNtpSamples started\n");
384     vnThreadsRunning[THREAD_NTP]++;
385
386     // Make this thread recognisable as time synchronization thread
387     RenameThread("novacoin-ntp-samples");
388
389     while (!fShutdown) {
390         CNetAddr ip;
391         int64_t nTime = NtpGetTime(ip);
392
393         if (nTime > 0 && nTime != 2085978496) { // Skip the deliberately wrong timestamps
394             AddTimeData(ip, nTime);
395         }
396         else {
397             Sleep(600000); // In case of failure wait 600 seconds and then try again
398             continue;
399         }
400
401         Sleep(43200000); // Sleep for 12 hours
402     }
403
404     vnThreadsRunning[THREAD_NTP]--;
405     printf("ThreadNtpSamples exited\n");
406 }