compat.h definitions should work well.
[novacoin.git] / src / ntp.cpp
1 #include <sys/time.h>
2
3 #ifdef WIN32
4 #include <winsock2.h>
5 #else
6 #include <sys/socket.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
17 extern int GetRandInt(int nMax);
18
19 /*
20  * NTP uses two fixed point formats.  The first (l_fp) is the "long"
21  * format and is 64 bits long with the decimal between bits 31 and 32.
22  * This is used for time stamps in the NTP packet header (in network
23  * byte order) and for internal computations of offsets (in local host
24  * byte order). We use the same structure for both signed and unsigned
25  * values, which is a big hack but saves rewriting all the operators
26  * twice. Just to confuse this, we also sometimes just carry the
27  * fractional part in calculations, in both signed and unsigned forms.
28  * Anyway, an l_fp looks like:
29  *
30  *    0                   1                   2                   3
31  *    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
32  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33  *   |                         Integral Part                         |
34  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35  *   |                         Fractional Part                       |
36  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37  * REF http://www.eecis.udel.edu/~mills/database/rfc/rfc2030.txt
38  */
39
40
41 typedef struct {
42   union {
43     uint32_t Xl_ui;
44     int32_t Xl_i;
45   } Ul_i;
46   union {
47     uint32_t Xl_uf;
48     int32_t Xl_f;
49   } Ul_f;
50 } l_fp;
51
52
53 inline void Ntp2Unix(uint32_t &n, time_t &u)
54 {
55     // Ntp's time scale starts in 1900, Unix in 1970.
56
57     u = n - 0x83aa7e80; // 2208988800 1970 - 1900 in seconds
58 }
59
60 inline void HTONL_FP(l_fp *h, l_fp *n)
61 {
62     (n)->Ul_i.Xl_ui = htonl((h)->Ul_i.Xl_ui);
63     (n)->Ul_f.Xl_uf = htonl((h)->Ul_f.Xl_uf);
64 }
65
66 inline void ntohl_fp(l_fp *n, l_fp *h)
67 {
68     (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui);
69     (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf);
70 }
71
72 struct pkt {
73   uint8_t  li_vn_mode;     /* leap indicator, version and mode */
74   uint8_t  stratum;        /* peer stratum */
75   uint8_t  ppoll;          /* peer poll interval */
76   int8_t  precision;      /* peer clock precision */
77   uint32_t    rootdelay;      /* distance to primary clock */
78   uint32_t    rootdispersion; /* clock dispersion */
79   uint32_t refid;          /* reference clock ID */
80   l_fp    ref;        /* time peer clock was last updated */
81   l_fp    org;            /* originate time stamp */
82   l_fp    rec;            /* receive time stamp */
83   l_fp    xmt;            /* transmit time stamp */
84
85   uint32_t exten[1];       /* misused */
86   uint8_t  mac[5 * sizeof(uint32_t)]; /* mac */
87 };
88
89 int nServersCount = 65;
90
91 std::string NtpServers[65] = {
92     // Microsoft
93     "time.windows.com",
94
95     // Google
96     "time1.google.com",
97     "time2.google.com",
98
99     // Russian Federation
100     "ntp.ix.ru",
101     "0.ru.pool.ntp.org",
102     "1.ru.pool.ntp.org",
103     "2.ru.pool.ntp.org",
104     "3.ru.pool.ntp.org",
105     "ntp1.stratum2.ru",
106     "ntp2.stratum2.ru",
107     "ntp3.stratum2.ru",
108     "ntp4.stratum2.ru",
109     "ntp5.stratum2.ru",
110     "ntp6.stratum2.ru",
111     "ntp7.stratum2.ru",
112     "ntp1.stratum1.ru",
113     "ntp2.stratum1.ru",
114     "ntp3.stratum1.ru",
115     "ntp4.stratum1.ru",
116     "ntp1.vniiftri.ru",
117     "ntp2.vniiftri.ru",
118     "ntp3.vniiftri.ru",
119     "ntp4.vniiftri.ru",
120     "ntp21.vniiftri.ru",
121     "ntp1.niiftri.irkutsk.ru",
122     "ntp2.niiftri.irkutsk.ru",
123     "vniiftri.khv.ru",
124     "vniiftri2.khv.ru",
125
126     // United States
127     "nist1-pa.ustiming.org",
128     "time-a.nist.gov ",
129     "time-b.nist.gov ",
130     "time-c.nist.gov ",
131     "time-d.nist.gov ",
132     "nist1-macon.macon.ga.us",
133     "nist.netservicesgroup.com",
134     "nisttime.carsoncity.k12.mi.us",
135     "nist1-lnk.binary.net",
136     "wwv.nist.gov",
137     "time-a.timefreq.bldrdoc.gov",
138     "time-b.timefreq.bldrdoc.gov",
139     "time-c.timefreq.bldrdoc.gov",
140     "time.nist.gov",
141     "utcnist.colorado.edu",
142     "utcnist2.colorado.edu",
143     "ntp-nist.ldsbc.net",
144     "nist1-lv.ustiming.org",
145     "time-nw.nist.gov",
146     "nist-time-server.eoni.com",
147     "nist-time-server.eoni.com",
148     "ntp1.bu.edu",
149     "ntp2.bu.edu",
150     "ntp3.bu.edu",
151
152     // South Africa
153     "ntp1.meraka.csir.co.za",
154     "ntp.is.co.za",
155     "ntp2.is.co.za",
156     "igubu.saix.net",
157     "ntp1.neology.co.za",
158     "ntp2.neology.co.za",
159     "tick.meraka.csir.co.za",
160     "tock.meraka.csir.co.za",
161     "ntp.time.org.za",
162     "ntp1.meraka.csir.co.za",
163     "ntp2.meraka.csir.co.za",
164
165     // Italy
166     "ntp1.inrim.it",
167     "ntp2.inrim.it",
168
169     // ... To be continued
170 };
171
172 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
173 {
174     int nAttempt = 0;
175
176     while(nAttempt < 100)
177     {
178         sockfd = -1;
179         nAttempt++;
180
181         int nServerNum = GetRandInt(nServersCount);
182
183         std::vector<CNetAddr> vIP;
184         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
185         if (!fRet)
186             continue;
187
188         struct sockaddr_in servaddr;
189         servaddr.sin_family = AF_INET;
190         servaddr.sin_port = htons(123);
191
192         bool found = false;
193         for(unsigned int i = 0; i < vIP.size(); i++)
194         {
195             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
196             {
197                 break;
198             }
199         }
200
201         if (!found)
202             continue;
203
204         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
205
206         if (sockfd == INVALID_SOCKET)
207             continue; // socket initialization error
208
209         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
210         {
211             continue; // "connection" error
212         }
213
214         *pcliaddr = *((struct sockaddr *) &servaddr);
215         servlen = sizeof(servaddr);
216         return true;
217     }
218
219     return false;
220 }
221
222 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
223 {
224     sockfd = -1;
225
226     std::vector<CNetAddr> vIP;
227     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
228     if (!fRet)
229         return false;
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         return false;
246
247     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
248
249     if (sockfd == INVALID_SOCKET)
250         return false; // socket initialization error
251
252     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
253     {
254         return false; // "connection" error
255     }
256
257     *pcliaddr = *((struct sockaddr *) &servaddr);
258     servlen = sizeof(servaddr);
259
260     return true;
261 }
262
263
264 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr)
265 {
266     struct pkt *msg = new pkt;
267     struct pkt *prt  = new pkt;
268
269     msg->li_vn_mode=227;
270     msg->stratum=0;
271     msg->ppoll=4;
272     msg->precision=0;
273     msg->rootdelay=0;
274     msg->rootdispersion=0;
275
276     msg->ref.Ul_i.Xl_i=0;
277     msg->ref.Ul_f.Xl_f=0;
278     msg->org.Ul_i.Xl_i=0;
279     msg->org.Ul_f.Xl_f=0;
280     msg->rec.Ul_i.Xl_i=0;
281     msg->rec.Ul_f.Xl_f=0;
282     msg->xmt.Ul_i.Xl_i=0;
283     msg->xmt.Ul_f.Xl_f=0;
284
285     int len=48;
286     sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
287     int n = recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
288
289     ntohl_fp(&msg->rec, &prt->rec);
290     ntohl_fp(&msg->xmt, &prt->xmt);
291
292     time_t seconds_receive;
293     time_t seconds_transmit;
294
295     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
296     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
297
298     delete msg;
299     delete prt;
300
301     return (seconds_receive + seconds_transmit) / 2;
302 }
303
304 int64_t NtpGetTime()
305 {
306     struct sockaddr cliaddr;
307
308     SOCKET sockfd;
309     socklen_t servlen;
310
311     if (!InitWithRandom(sockfd, servlen, &cliaddr))
312         return -1;
313
314     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
315
316     closesocket(sockfd);
317
318     return nTime;
319 }
320
321 int64_t NtpGetTime(std::string &strHostName)
322 {
323     struct sockaddr cliaddr;
324
325     SOCKET sockfd;
326     socklen_t servlen;
327
328     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
329         return -1;
330
331     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
332
333     closesocket(sockfd);
334
335     return nTime;
336 }