Add some public 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
18 extern int GetRandInt(int nMax);
19
20 /*
21  * NTP uses two fixed point formats.  The first (l_fp) is the "long"
22  * format and is 64 bits long with the decimal between bits 31 and 32.
23  * This is used for time stamps in the NTP packet header (in network
24  * byte order) and for internal computations of offsets (in local host
25  * byte order). We use the same structure for both signed and unsigned
26  * values, which is a big hack but saves rewriting all the operators
27  * twice. Just to confuse this, we also sometimes just carry the
28  * fractional part in calculations, in both signed and unsigned forms.
29  * Anyway, an l_fp looks like:
30  *
31  *    0                   1                   2                   3
32  *    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
33  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34  *   |                         Integral Part                         |
35  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36  *   |                         Fractional Part                       |
37  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38  * REF http://www.eecis.udel.edu/~mills/database/rfc/rfc2030.txt
39  */
40
41
42 typedef struct {
43   union {
44     uint32_t Xl_ui;
45     int32_t Xl_i;
46   } Ul_i;
47   union {
48     uint32_t Xl_uf;
49     int32_t Xl_f;
50   } Ul_f;
51 } l_fp;
52
53
54 inline void Ntp2Unix(uint32_t &n, time_t &u)
55 {
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 {
63     (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui);
64     (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf);
65 }
66
67 struct pkt {
68   uint8_t  li_vn_mode;     /* leap indicator, version and mode */
69   uint8_t  stratum;        /* peer stratum */
70   uint8_t  ppoll;          /* peer poll interval */
71   int8_t  precision;      /* peer clock precision */
72   uint32_t    rootdelay;      /* distance to primary clock */
73   uint32_t    rootdispersion; /* clock dispersion */
74   uint32_t refid;          /* reference clock ID */
75   l_fp    ref;        /* time peer clock was last updated */
76   l_fp    org;            /* originate time stamp */
77   l_fp    rec;            /* receive time stamp */
78   l_fp    xmt;            /* transmit time stamp */
79
80   uint32_t exten[1];       /* misused */
81   uint8_t  mac[5 * sizeof(uint32_t)]; /* mac */
82 };
83
84 int nServersCount = 103;
85
86 std::string NtpServers[103] = {
87     // Microsoft
88     "time.windows.com",
89
90     // Google
91     "time1.google.com",
92     "time2.google.com",
93     "time3.google.com",
94     "time4.google.com",
95
96     // Russian Federation
97     "ntp.ix.ru",
98     "ntp1.stratum2.ru",
99     "ntp2.stratum2.ru",
100     "ntp3.stratum2.ru",
101     "ntp4.stratum2.ru",
102     "ntp5.stratum2.ru",
103     "ntp6.stratum2.ru",
104     "ntp7.stratum2.ru",
105     "ntp1.stratum1.ru",
106     "ntp2.stratum1.ru",
107     "ntp3.stratum1.ru",
108     "ntp4.stratum1.ru",
109     "ntp1.vniiftri.ru",
110     "ntp2.vniiftri.ru",
111     "ntp3.vniiftri.ru",
112     "ntp4.vniiftri.ru",
113     "ntp21.vniiftri.ru",
114     "ntp1.niiftri.irkutsk.ru",
115     "ntp2.niiftri.irkutsk.ru",
116     "vniiftri.khv.ru",
117     "vniiftri2.khv.ru",
118     "ntp0.zenon.net",
119     "ntp.mobatime.ru",
120     "0.ru.pool.ntp.org",
121     "1.ru.pool.ntp.org",
122     "2.ru.pool.ntp.org",
123     "3.ru.pool.ntp.org",
124
125     // United States
126     "nist1-pa.ustiming.org",
127     "time-a.nist.gov ",
128     "time-b.nist.gov ",
129     "time-c.nist.gov ",
130     "time-d.nist.gov ",
131     "nist1-macon.macon.ga.us",
132     "nist.netservicesgroup.com",
133     "nisttime.carsoncity.k12.mi.us",
134     "nist1-lnk.binary.net",
135     "wwv.nist.gov",
136     "time-a.timefreq.bldrdoc.gov",
137     "time-b.timefreq.bldrdoc.gov",
138     "time-c.timefreq.bldrdoc.gov",
139     "time.nist.gov",
140     "utcnist.colorado.edu",
141     "utcnist2.colorado.edu",
142     "ntp-nist.ldsbc.net",
143     "nist1-lv.ustiming.org",
144     "time-nw.nist.gov",
145     "nist-time-server.eoni.com",
146     "nist-time-server.eoni.com",
147     "ntp1.bu.edu",
148     "ntp2.bu.edu",
149     "ntp3.bu.edu",
150     "0.us.pool.ntp.org",
151     "1.us.pool.ntp.org",
152     "2.us.pool.ntp.org",
153     "3.us.pool.ntp.org",
154
155     // South Africa
156     "ntp1.meraka.csir.co.za",
157     "ntp.is.co.za",
158     "ntp2.is.co.za",
159     "igubu.saix.net",
160     "ntp1.neology.co.za",
161     "ntp2.neology.co.za",
162     "tick.meraka.csir.co.za",
163     "tock.meraka.csir.co.za",
164     "ntp.time.org.za",
165     "ntp1.meraka.csir.co.za",
166     "ntp2.meraka.csir.co.za",
167     "0.za.pool.ntp.org",
168     "1.za.pool.ntp.org",
169     "2.za.pool.ntp.org",
170     "3.za.pool.ntp.org",
171
172     // Italy
173     "ntp1.inrim.it",
174     "ntp2.inrim.it",
175     "0.it.pool.ntp.org",
176     "1.it.pool.ntp.org",
177     "2.it.pool.ntp.org",
178     "3.it.pool.ntp.org",
179
180     // Netherlands
181     "ntp0.nl.net",
182     "ntp1.nl.net",
183     "ntp2.nl.net",
184     "ntp.utwente.nl",
185     "0.nl.pool.ntp.org",
186     "1.nl.pool.ntp.org",
187     "2.nl.pool.ntp.org",
188     "3.nl.pool.ntp.org",
189
190     // United Kingdom
191     "ntp2d.mcc.ac.uk",
192     "ntp2c.mcc.ac.uk",
193     "ntp.exnet.com",
194     "ntp.cis.strath.ac.uk",
195     "ntppub.le.ac.uk",
196     "0.uk.pool.ntp.org",
197     "1.uk.pool.ntp.org",
198     "2.uk.pool.ntp.org",
199     "3.uk.pool.ntp.org",
200
201     // Japan
202     "ntp.nict.jp",
203     "0.jp.pool.ntp.org",
204     "1.jp.pool.ntp.org",
205     "2.jp.pool.ntp.org",
206     "3.jp.pool.ntp.org",
207
208     // ... To be continued
209 };
210
211 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
212 {
213     int nAttempt = 0;
214
215     while(nAttempt < 100)
216     {
217         sockfd = -1;
218         nAttempt++;
219
220         int nServerNum = GetRandInt(nServersCount);
221
222         std::vector<CNetAddr> vIP;
223         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
224         if (!fRet)
225             continue;
226
227         struct sockaddr_in servaddr;
228         servaddr.sin_family = AF_INET;
229         servaddr.sin_port = htons(123);
230
231         bool found = false;
232         for(unsigned int i = 0; i < vIP.size(); i++)
233         {
234             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
235             {
236                 break;
237             }
238         }
239
240         if (!found)
241             continue;
242
243         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
244
245         if (sockfd == INVALID_SOCKET)
246             continue; // socket initialization error
247
248         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
249         {
250             continue; // "connection" error
251         }
252
253         *pcliaddr = *((struct sockaddr *) &servaddr);
254         servlen = sizeof(servaddr);
255         return true;
256     }
257
258     return false;
259 }
260
261 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
262 {
263     sockfd = -1;
264
265     std::vector<CNetAddr> vIP;
266     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
267     if (!fRet)
268         return false;
269
270     struct sockaddr_in servaddr;
271     servaddr.sin_family = AF_INET;
272     servaddr.sin_port = htons(123);
273
274     bool found = false;
275     for(unsigned int i = 0; i < vIP.size(); i++)
276     {
277         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
278         {
279             break;
280         }
281     }
282
283     if (!found)
284         return false;
285
286     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
287
288     if (sockfd == INVALID_SOCKET)
289         return false; // socket initialization error
290
291     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
292     {
293         return false; // "connection" error
294     }
295
296
297     *pcliaddr = *((struct sockaddr *) &servaddr);
298     servlen = sizeof(servaddr);
299
300     return true;
301 }
302
303
304 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr)
305 {
306
307 #ifdef WIN32
308     u_long nOne = 1;
309     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR)
310     {
311         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
312 #else
313     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
314     {
315         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
316 #endif
317         return -2;
318     }
319
320     struct pkt *msg = new pkt;
321     struct pkt *prt  = new pkt;
322
323     msg->li_vn_mode=227;
324     msg->stratum=0;
325     msg->ppoll=4;
326     msg->precision=0;
327     msg->rootdelay=0;
328     msg->rootdispersion=0;
329
330     msg->ref.Ul_i.Xl_i=0;
331     msg->ref.Ul_f.Xl_f=0;
332     msg->org.Ul_i.Xl_i=0;
333     msg->org.Ul_f.Xl_f=0;
334     msg->rec.Ul_i.Xl_i=0;
335     msg->rec.Ul_f.Xl_f=0;
336     msg->xmt.Ul_i.Xl_i=0;
337     msg->xmt.Ul_f.Xl_f=0;
338
339     int len=48;
340     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
341     if (retcode < 0) {
342         printf("sendto() failed: %d", retcode);
343         return -3;
344     }
345
346     fd_set fdset;
347     struct timeval timeout = {5, 0};
348     FD_ZERO(&fdset);
349     FD_SET(sockfd, &fdset);
350
351     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
352     if (retcode <= 0)
353     {
354         printf("recvfrom() error");
355         return -4;
356     }
357
358     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
359
360     ntohl_fp(&msg->rec, &prt->rec);
361     ntohl_fp(&msg->xmt, &prt->xmt);
362
363     time_t seconds_receive;
364     time_t seconds_transmit;
365
366     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
367     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
368
369     delete msg;
370     delete prt;
371
372     return (seconds_receive + seconds_transmit) / 2;
373 }
374
375 int64_t NtpGetTime()
376 {
377     struct sockaddr cliaddr;
378
379     SOCKET sockfd;
380     socklen_t servlen;
381
382     if (!InitWithRandom(sockfd, servlen, &cliaddr))
383         return -1;
384
385     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
386
387     closesocket(sockfd);
388
389     return nTime;
390 }
391
392 int64_t NtpGetTime(std::string &strHostName)
393 {
394     struct sockaddr cliaddr;
395
396     SOCKET sockfd;
397     socklen_t servlen;
398
399     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
400         return -1;
401
402     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
403
404     closesocket(sockfd);
405
406     return nTime;
407 }