Add Caltech time 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 = 107;
85
86 std::string NtpServers[107] = {
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     "ntp-01.caltech.edu",
127     "ntp-02.caltech.edu",
128     "ntp-03.caltech.edu",
129     "ntp-04.caltech.edu",
130     "nist1-pa.ustiming.org",
131     "time-a.nist.gov ",
132     "time-b.nist.gov ",
133     "time-c.nist.gov ",
134     "time-d.nist.gov ",
135     "nist1-macon.macon.ga.us",
136     "nist.netservicesgroup.com",
137     "nisttime.carsoncity.k12.mi.us",
138     "nist1-lnk.binary.net",
139     "wwv.nist.gov",
140     "time-a.timefreq.bldrdoc.gov",
141     "time-b.timefreq.bldrdoc.gov",
142     "time-c.timefreq.bldrdoc.gov",
143     "time.nist.gov",
144     "utcnist.colorado.edu",
145     "utcnist2.colorado.edu",
146     "ntp-nist.ldsbc.net",
147     "nist1-lv.ustiming.org",
148     "time-nw.nist.gov",
149     "nist-time-server.eoni.com",
150     "nist-time-server.eoni.com",
151     "ntp1.bu.edu",
152     "ntp2.bu.edu",
153     "ntp3.bu.edu",
154     "0.us.pool.ntp.org",
155     "1.us.pool.ntp.org",
156     "2.us.pool.ntp.org",
157     "3.us.pool.ntp.org",
158
159     // South Africa
160     "ntp1.meraka.csir.co.za",
161     "ntp.is.co.za",
162     "ntp2.is.co.za",
163     "igubu.saix.net",
164     "ntp1.neology.co.za",
165     "ntp2.neology.co.za",
166     "tick.meraka.csir.co.za",
167     "tock.meraka.csir.co.za",
168     "ntp.time.org.za",
169     "ntp1.meraka.csir.co.za",
170     "ntp2.meraka.csir.co.za",
171     "0.za.pool.ntp.org",
172     "1.za.pool.ntp.org",
173     "2.za.pool.ntp.org",
174     "3.za.pool.ntp.org",
175
176     // Italy
177     "ntp1.inrim.it",
178     "ntp2.inrim.it",
179     "0.it.pool.ntp.org",
180     "1.it.pool.ntp.org",
181     "2.it.pool.ntp.org",
182     "3.it.pool.ntp.org",
183
184     // Netherlands
185     "ntp0.nl.net",
186     "ntp1.nl.net",
187     "ntp2.nl.net",
188     "ntp.utwente.nl",
189     "0.nl.pool.ntp.org",
190     "1.nl.pool.ntp.org",
191     "2.nl.pool.ntp.org",
192     "3.nl.pool.ntp.org",
193
194     // United Kingdom
195     "ntp2d.mcc.ac.uk",
196     "ntp2c.mcc.ac.uk",
197     "ntp.exnet.com",
198     "ntp.cis.strath.ac.uk",
199     "ntppub.le.ac.uk",
200     "0.uk.pool.ntp.org",
201     "1.uk.pool.ntp.org",
202     "2.uk.pool.ntp.org",
203     "3.uk.pool.ntp.org",
204
205     // Japan
206     "ntp.nict.jp",
207     "0.jp.pool.ntp.org",
208     "1.jp.pool.ntp.org",
209     "2.jp.pool.ntp.org",
210     "3.jp.pool.ntp.org",
211
212     // ... To be continued
213 };
214
215 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
216 {
217     int nAttempt = 0;
218
219     while(nAttempt < 100)
220     {
221         sockfd = -1;
222         nAttempt++;
223
224         int nServerNum = GetRandInt(nServersCount);
225
226         std::vector<CNetAddr> vIP;
227         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
228         if (!fRet)
229             continue;
230
231         struct sockaddr_in servaddr;
232         servaddr.sin_family = AF_INET;
233         servaddr.sin_port = htons(123);
234
235         bool found = false;
236         for(unsigned int i = 0; i < vIP.size(); i++)
237         {
238             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
239             {
240                 break;
241             }
242         }
243
244         if (!found)
245             continue;
246
247         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
248
249         if (sockfd == INVALID_SOCKET)
250             continue; // socket initialization error
251
252         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
253         {
254             continue; // "connection" error
255         }
256
257         *pcliaddr = *((struct sockaddr *) &servaddr);
258         servlen = sizeof(servaddr);
259         return true;
260     }
261
262     return false;
263 }
264
265 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
266 {
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     struct sockaddr_in servaddr;
275     servaddr.sin_family = AF_INET;
276     servaddr.sin_port = htons(123);
277
278     bool found = false;
279     for(unsigned int i = 0; i < vIP.size(); i++)
280     {
281         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
282         {
283             break;
284         }
285     }
286
287     if (!found)
288         return false;
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     {
297         return false; // "connection" error
298     }
299
300
301     *pcliaddr = *((struct sockaddr *) &servaddr);
302     servlen = sizeof(servaddr);
303
304     return true;
305 }
306
307
308 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr)
309 {
310
311 #ifdef WIN32
312     u_long nOne = 1;
313     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR)
314     {
315         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
316 #else
317     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
318     {
319         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
320 #endif
321         return -2;
322     }
323
324     struct pkt *msg = new pkt;
325     struct pkt *prt  = new pkt;
326
327     msg->li_vn_mode=227;
328     msg->stratum=0;
329     msg->ppoll=4;
330     msg->precision=0;
331     msg->rootdelay=0;
332     msg->rootdispersion=0;
333
334     msg->ref.Ul_i.Xl_i=0;
335     msg->ref.Ul_f.Xl_f=0;
336     msg->org.Ul_i.Xl_i=0;
337     msg->org.Ul_f.Xl_f=0;
338     msg->rec.Ul_i.Xl_i=0;
339     msg->rec.Ul_f.Xl_f=0;
340     msg->xmt.Ul_i.Xl_i=0;
341     msg->xmt.Ul_f.Xl_f=0;
342
343     int len=48;
344     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
345     if (retcode < 0) {
346         printf("sendto() failed: %d", retcode);
347         return -3;
348     }
349
350     fd_set fdset;
351     struct timeval timeout = {5, 0};
352     FD_ZERO(&fdset);
353     FD_SET(sockfd, &fdset);
354
355     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
356     if (retcode <= 0)
357     {
358         printf("recvfrom() error");
359         return -4;
360     }
361
362     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
363
364     ntohl_fp(&msg->rec, &prt->rec);
365     ntohl_fp(&msg->xmt, &prt->xmt);
366
367     time_t seconds_receive;
368     time_t seconds_transmit;
369
370     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
371     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
372
373     delete msg;
374     delete prt;
375
376     return (seconds_receive + seconds_transmit) / 2;
377 }
378
379 int64_t NtpGetTime()
380 {
381     struct sockaddr cliaddr;
382
383     SOCKET sockfd;
384     socklen_t servlen;
385
386     if (!InitWithRandom(sockfd, servlen, &cliaddr))
387         return -1;
388
389     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
390
391     closesocket(sockfd);
392
393     return nTime;
394 }
395
396 int64_t NtpGetTime(CNetAddr& ip)
397 {
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 }