Merge pull request #232 from svost/solution-ntp
[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 = 65;
85
86 std::string NtpServers[65] = {
87     // Microsoft
88     "time.windows.com",
89
90     // Google
91     "time1.google.com",
92     "time2.google.com",
93
94     // Russian Federation
95     "ntp.ix.ru",
96     "0.ru.pool.ntp.org",
97     "1.ru.pool.ntp.org",
98     "2.ru.pool.ntp.org",
99     "3.ru.pool.ntp.org",
100     "ntp1.stratum2.ru",
101     "ntp2.stratum2.ru",
102     "ntp3.stratum2.ru",
103     "ntp4.stratum2.ru",
104     "ntp5.stratum2.ru",
105     "ntp6.stratum2.ru",
106     "ntp7.stratum2.ru",
107     "ntp1.stratum1.ru",
108     "ntp2.stratum1.ru",
109     "ntp3.stratum1.ru",
110     "ntp4.stratum1.ru",
111     "ntp1.vniiftri.ru",
112     "ntp2.vniiftri.ru",
113     "ntp3.vniiftri.ru",
114     "ntp4.vniiftri.ru",
115     "ntp21.vniiftri.ru",
116     "ntp1.niiftri.irkutsk.ru",
117     "ntp2.niiftri.irkutsk.ru",
118     "vniiftri.khv.ru",
119     "vniiftri2.khv.ru",
120
121     // United States
122     "nist1-pa.ustiming.org",
123     "time-a.nist.gov ",
124     "time-b.nist.gov ",
125     "time-c.nist.gov ",
126     "time-d.nist.gov ",
127     "nist1-macon.macon.ga.us",
128     "nist.netservicesgroup.com",
129     "nisttime.carsoncity.k12.mi.us",
130     "nist1-lnk.binary.net",
131     "wwv.nist.gov",
132     "time-a.timefreq.bldrdoc.gov",
133     "time-b.timefreq.bldrdoc.gov",
134     "time-c.timefreq.bldrdoc.gov",
135     "time.nist.gov",
136     "utcnist.colorado.edu",
137     "utcnist2.colorado.edu",
138     "ntp-nist.ldsbc.net",
139     "nist1-lv.ustiming.org",
140     "time-nw.nist.gov",
141     "nist-time-server.eoni.com",
142     "nist-time-server.eoni.com",
143     "ntp1.bu.edu",
144     "ntp2.bu.edu",
145     "ntp3.bu.edu",
146
147     // South Africa
148     "ntp1.meraka.csir.co.za",
149     "ntp.is.co.za",
150     "ntp2.is.co.za",
151     "igubu.saix.net",
152     "ntp1.neology.co.za",
153     "ntp2.neology.co.za",
154     "tick.meraka.csir.co.za",
155     "tock.meraka.csir.co.za",
156     "ntp.time.org.za",
157     "ntp1.meraka.csir.co.za",
158     "ntp2.meraka.csir.co.za",
159
160     // Italy
161     "ntp1.inrim.it",
162     "ntp2.inrim.it",
163
164     // ... To be continued
165 };
166
167 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
168 {
169     int nAttempt = 0;
170
171     while(nAttempt < 100)
172     {
173         sockfd = -1;
174         nAttempt++;
175
176         int nServerNum = GetRandInt(nServersCount);
177
178         std::vector<CNetAddr> vIP;
179         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
180         if (!fRet)
181             continue;
182
183         struct sockaddr_in servaddr;
184         servaddr.sin_family = AF_INET;
185         servaddr.sin_port = htons(123);
186
187         bool found = false;
188         for(unsigned int i = 0; i < vIP.size(); i++)
189         {
190             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
191             {
192                 break;
193             }
194         }
195
196         if (!found)
197             continue;
198
199         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
200
201         if (sockfd == INVALID_SOCKET)
202             continue; // socket initialization error
203
204         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
205         {
206             continue; // "connection" error
207         }
208
209         *pcliaddr = *((struct sockaddr *) &servaddr);
210         servlen = sizeof(servaddr);
211         return true;
212     }
213
214     return false;
215 }
216
217 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
218 {
219     sockfd = -1;
220
221     std::vector<CNetAddr> vIP;
222     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
223     if (!fRet)
224         return false;
225
226     struct sockaddr_in servaddr;
227     servaddr.sin_family = AF_INET;
228     servaddr.sin_port = htons(123);
229
230     bool found = false;
231     for(unsigned int i = 0; i < vIP.size(); i++)
232     {
233         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
234         {
235             break;
236         }
237     }
238
239     if (!found)
240         return false;
241
242     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
243
244     if (sockfd == INVALID_SOCKET)
245         return false; // socket initialization error
246
247     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
248     {
249         return false; // "connection" error
250     }
251
252
253     *pcliaddr = *((struct sockaddr *) &servaddr);
254     servlen = sizeof(servaddr);
255
256     return true;
257 }
258
259
260 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr)
261 {
262
263 #ifdef WIN32
264     u_long nOne = 1;
265     if (ioctlsocket(sockfd, FIONBIO, &nOne) == SOCKET_ERROR)
266     {
267         printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError());
268 #else
269     if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
270     {
271         printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno);
272 #endif
273         return -2;
274     }
275
276     struct pkt *msg = new pkt;
277     struct pkt *prt  = new pkt;
278
279     msg->li_vn_mode=227;
280     msg->stratum=0;
281     msg->ppoll=4;
282     msg->precision=0;
283     msg->rootdelay=0;
284     msg->rootdispersion=0;
285
286     msg->ref.Ul_i.Xl_i=0;
287     msg->ref.Ul_f.Xl_f=0;
288     msg->org.Ul_i.Xl_i=0;
289     msg->org.Ul_f.Xl_f=0;
290     msg->rec.Ul_i.Xl_i=0;
291     msg->rec.Ul_f.Xl_f=0;
292     msg->xmt.Ul_i.Xl_i=0;
293     msg->xmt.Ul_f.Xl_f=0;
294
295     int len=48;
296     int retcode = sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
297     if (retcode < 0) {
298         printf("sendto() failed: %d", retcode);
299         return -3;
300     }
301
302     retcode = 0;
303     int nWait = 0;
304
305     while(retcode <= 0) {
306         Sleep(1000);
307         retcode = recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
308
309         if (nWait > 4) {
310             printf("recvfrom() timeout");
311             return -4;
312         }
313
314         nWait++;
315     }
316
317     ntohl_fp(&msg->rec, &prt->rec);
318     ntohl_fp(&msg->xmt, &prt->xmt);
319
320     time_t seconds_receive;
321     time_t seconds_transmit;
322
323     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
324     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
325
326     delete msg;
327     delete prt;
328
329     return (seconds_receive + seconds_transmit) / 2;
330 }
331
332 int64_t NtpGetTime()
333 {
334     struct sockaddr cliaddr;
335
336     SOCKET sockfd;
337     socklen_t servlen;
338
339     if (!InitWithRandom(sockfd, servlen, &cliaddr))
340         return -1;
341
342     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
343
344     closesocket(sockfd);
345
346     return nTime;
347 }
348
349 int64_t NtpGetTime(std::string &strHostName)
350 {
351     struct sockaddr cliaddr;
352
353     SOCKET sockfd;
354     socklen_t servlen;
355
356     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
357         return -1;
358
359     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
360
361     closesocket(sockfd);
362
363     return nTime;
364 }