7ec195c1d6e1deb02e21bbe81ded7e92e32ebeab
[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     fd_set fdset;
303     struct timeval timeout = {5, 0};
304     FD_ZERO(&fdset);
305     FD_SET(sockfd, &fdset);
306
307     retcode = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
308     if (retcode <= 0)
309     {
310         printf("recvfrom() error");
311         return -4;
312     }
313
314     recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
315
316     ntohl_fp(&msg->rec, &prt->rec);
317     ntohl_fp(&msg->xmt, &prt->xmt);
318
319     time_t seconds_receive;
320     time_t seconds_transmit;
321
322     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
323     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
324
325     delete msg;
326     delete prt;
327
328     return (seconds_receive + seconds_transmit) / 2;
329 }
330
331 int64_t NtpGetTime()
332 {
333     struct sockaddr cliaddr;
334
335     SOCKET sockfd;
336     socklen_t servlen;
337
338     if (!InitWithRandom(sockfd, servlen, &cliaddr))
339         return -1;
340
341     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
342
343     closesocket(sockfd);
344
345     return nTime;
346 }
347
348 int64_t NtpGetTime(std::string &strHostName)
349 {
350     struct sockaddr cliaddr;
351
352     SOCKET sockfd;
353     socklen_t servlen;
354
355     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
356         return -1;
357
358     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
359
360     closesocket(sockfd);
361
362     return nTime;
363 }