Update novacoin.solution concerning to tiny ntp client update #5a73a5b
[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
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 ntohl_fp(l_fp *n, l_fp *h)
61 {
62     (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui);
63     (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf);
64 }
65
66 struct pkt {
67   uint8_t  li_vn_mode;     /* leap indicator, version and mode */
68   uint8_t  stratum;        /* peer stratum */
69   uint8_t  ppoll;          /* peer poll interval */
70   int8_t  precision;      /* peer clock precision */
71   uint32_t    rootdelay;      /* distance to primary clock */
72   uint32_t    rootdispersion; /* clock dispersion */
73   uint32_t refid;          /* reference clock ID */
74   l_fp    ref;        /* time peer clock was last updated */
75   l_fp    org;            /* originate time stamp */
76   l_fp    rec;            /* receive time stamp */
77   l_fp    xmt;            /* transmit time stamp */
78
79   uint32_t exten[1];       /* misused */
80   uint8_t  mac[5 * sizeof(uint32_t)]; /* mac */
81 };
82
83 int nServersCount = 65;
84
85 std::string NtpServers[65] = {
86     // Microsoft
87     "time.windows.com",
88
89     // Google
90     "time1.google.com",
91     "time2.google.com",
92
93     // Russian Federation
94     "ntp.ix.ru",
95     "0.ru.pool.ntp.org",
96     "1.ru.pool.ntp.org",
97     "2.ru.pool.ntp.org",
98     "3.ru.pool.ntp.org",
99     "ntp1.stratum2.ru",
100     "ntp2.stratum2.ru",
101     "ntp3.stratum2.ru",
102     "ntp4.stratum2.ru",
103     "ntp5.stratum2.ru",
104     "ntp6.stratum2.ru",
105     "ntp7.stratum2.ru",
106     "ntp1.stratum1.ru",
107     "ntp2.stratum1.ru",
108     "ntp3.stratum1.ru",
109     "ntp4.stratum1.ru",
110     "ntp1.vniiftri.ru",
111     "ntp2.vniiftri.ru",
112     "ntp3.vniiftri.ru",
113     "ntp4.vniiftri.ru",
114     "ntp21.vniiftri.ru",
115     "ntp1.niiftri.irkutsk.ru",
116     "ntp2.niiftri.irkutsk.ru",
117     "vniiftri.khv.ru",
118     "vniiftri2.khv.ru",
119
120     // United States
121     "nist1-pa.ustiming.org",
122     "time-a.nist.gov ",
123     "time-b.nist.gov ",
124     "time-c.nist.gov ",
125     "time-d.nist.gov ",
126     "nist1-macon.macon.ga.us",
127     "nist.netservicesgroup.com",
128     "nisttime.carsoncity.k12.mi.us",
129     "nist1-lnk.binary.net",
130     "wwv.nist.gov",
131     "time-a.timefreq.bldrdoc.gov",
132     "time-b.timefreq.bldrdoc.gov",
133     "time-c.timefreq.bldrdoc.gov",
134     "time.nist.gov",
135     "utcnist.colorado.edu",
136     "utcnist2.colorado.edu",
137     "ntp-nist.ldsbc.net",
138     "nist1-lv.ustiming.org",
139     "time-nw.nist.gov",
140     "nist-time-server.eoni.com",
141     "nist-time-server.eoni.com",
142     "ntp1.bu.edu",
143     "ntp2.bu.edu",
144     "ntp3.bu.edu",
145
146     // South Africa
147     "ntp1.meraka.csir.co.za",
148     "ntp.is.co.za",
149     "ntp2.is.co.za",
150     "igubu.saix.net",
151     "ntp1.neology.co.za",
152     "ntp2.neology.co.za",
153     "tick.meraka.csir.co.za",
154     "tock.meraka.csir.co.za",
155     "ntp.time.org.za",
156     "ntp1.meraka.csir.co.za",
157     "ntp2.meraka.csir.co.za",
158
159     // Italy
160     "ntp1.inrim.it",
161     "ntp2.inrim.it",
162
163     // ... To be continued
164 };
165
166 bool InitWithRandom(SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
167 {
168     int nAttempt = 0;
169
170     while(nAttempt < 100)
171     {
172         sockfd = -1;
173         nAttempt++;
174
175         int nServerNum = GetRandInt(nServersCount);
176
177         std::vector<CNetAddr> vIP;
178         bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true);
179         if (!fRet)
180             continue;
181
182         struct sockaddr_in servaddr;
183         servaddr.sin_family = AF_INET;
184         servaddr.sin_port = htons(123);
185
186         bool found = false;
187         for(unsigned int i = 0; i < vIP.size(); i++)
188         {
189             if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
190             {
191                 break;
192             }
193         }
194
195         if (!found)
196             continue;
197
198         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
199
200         if (sockfd == INVALID_SOCKET)
201             continue; // socket initialization error
202
203         if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
204         {
205             continue; // "connection" error
206         }
207
208         *pcliaddr = *((struct sockaddr *) &servaddr);
209         servlen = sizeof(servaddr);
210         return true;
211     }
212
213     return false;
214 }
215
216 bool InitWithHost(std::string &strHostName, SOCKET &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr)
217 {
218     sockfd = -1;
219
220     std::vector<CNetAddr> vIP;
221     bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true);
222     if (!fRet)
223         return false;
224
225     struct sockaddr_in servaddr;
226     servaddr.sin_family = AF_INET;
227     servaddr.sin_port = htons(123);
228
229     bool found = false;
230     for(unsigned int i = 0; i < vIP.size(); i++)
231     {
232         if ((found = vIP[i].GetInAddr(&servaddr.sin_addr)))
233         {
234             break;
235         }
236     }
237
238     if (!found)
239         return false;
240
241     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
242
243     if (sockfd == INVALID_SOCKET)
244         return false; // socket initialization error
245
246     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 )
247     {
248         return false; // "connection" error
249     }
250
251     *pcliaddr = *((struct sockaddr *) &servaddr);
252     servlen = sizeof(servaddr);
253
254     return true;
255 }
256
257
258 int64_t DoReq(SOCKET sockfd, socklen_t servlen, struct sockaddr cliaddr)
259 {
260     struct pkt *msg = new pkt;
261     struct pkt *prt  = new pkt;
262
263     msg->li_vn_mode=227;
264     msg->stratum=0;
265     msg->ppoll=4;
266     msg->precision=0;
267     msg->rootdelay=0;
268     msg->rootdispersion=0;
269
270     msg->ref.Ul_i.Xl_i=0;
271     msg->ref.Ul_f.Xl_f=0;
272     msg->org.Ul_i.Xl_i=0;
273     msg->org.Ul_f.Xl_f=0;
274     msg->rec.Ul_i.Xl_i=0;
275     msg->rec.Ul_f.Xl_f=0;
276     msg->xmt.Ul_i.Xl_i=0;
277     msg->xmt.Ul_f.Xl_f=0;
278
279     int len=48;
280     sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen);
281     int n = recvfrom(sockfd, (char *) msg, len, 0, NULL, NULL);
282
283     ntohl_fp(&msg->rec, &prt->rec);
284     ntohl_fp(&msg->xmt, &prt->xmt);
285
286     time_t seconds_receive;
287     time_t seconds_transmit;
288
289     Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive);
290     Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit);
291
292     delete msg;
293     delete prt;
294
295     return (seconds_receive + seconds_transmit) / 2;
296 }
297
298 int64_t NtpGetTime()
299 {
300     struct sockaddr cliaddr;
301
302     SOCKET sockfd;
303     socklen_t servlen;
304
305     if (!InitWithRandom(sockfd, servlen, &cliaddr))
306         return -1;
307
308     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
309
310     closesocket(sockfd);
311
312     return nTime;
313 }
314
315 int64_t NtpGetTime(std::string &strHostName)
316 {
317     struct sockaddr cliaddr;
318
319     SOCKET sockfd;
320     socklen_t servlen;
321
322     if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr))
323         return -1;
324
325     int64_t nTime = DoReq(sockfd, servlen, cliaddr);
326
327     closesocket(sockfd);
328
329     return nTime;
330 }