From 5a73a5bcab66ec1012ac88b4d61bdc1e0213286c Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Thu, 17 Sep 2015 02:23:48 +0300 Subject: [PATCH] Tiny NTP client. --- novacoin-qt.pro | 2 + src/bitcoinrpc.cpp | 1 + src/bitcoinrpc.h | 1 + src/makefile.unix | 1 + src/ntp.cpp | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ntp.h | 3 + src/rpcnet.cpp | 28 ++++- 7 files changed, 399 insertions(+), 1 deletions(-) create mode 100644 src/ntp.cpp create mode 100644 src/ntp.h diff --git a/novacoin-qt.pro b/novacoin-qt.pro index 1972f70..9d492ac 100644 --- a/novacoin-qt.pro +++ b/novacoin-qt.pro @@ -264,6 +264,7 @@ HEADERS += src/qt/bitcoingui.h \ src/ui_interface.h \ src/qt/rpcconsole.h \ src/version.h \ + src/ntp.h \ src/netbase.h \ src/clientversion.h \ src/qt/multisigaddressentry.h \ @@ -294,6 +295,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/sync.cpp \ src/util.cpp \ src/netbase.cpp \ + src/ntp.cpp \ src/key.cpp \ src/script.cpp \ src/main.cpp \ diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 1f55825..8e21521 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -253,6 +253,7 @@ static const CRPCCommand vRPCCommands[] = { "scaninput", &scaninput, true, true }, { "getnewaddress", &getnewaddress, true, false }, { "getnettotals", &getnettotals, true, true }, + { "ntptime", &ntptime, true, true }, { "getaccountaddress", &getaccountaddress, true, false }, { "setaccount", &setaccount, true, false }, { "getaccount", &getaccount, false, false }, diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 8a61329..ad64a84 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -156,6 +156,7 @@ extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool f extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value removeaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value ntptime(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp); diff --git a/src/makefile.unix b/src/makefile.unix index dfbb6ec..a4630c0 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -119,6 +119,7 @@ OBJS= \ obj/miner.o \ obj/main.o \ obj/net.o \ + obj/ntp.o \ obj/stun.o \ obj/protocol.o \ obj/bitcoinrpc.o \ diff --git a/src/ntp.cpp b/src/ntp.cpp new file mode 100644 index 0000000..b60e80d --- /dev/null +++ b/src/ntp.cpp @@ -0,0 +1,364 @@ +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif +#ifndef WIN32 +#include +#endif + +#include "netbase.h" + +extern int GetRandInt(int nMax); + +/* + * NTP uses two fixed point formats. The first (l_fp) is the "long" + * format and is 64 bits long with the decimal between bits 31 and 32. + * This is used for time stamps in the NTP packet header (in network + * byte order) and for internal computations of offsets (in local host + * byte order). We use the same structure for both signed and unsigned + * values, which is a big hack but saves rewriting all the operators + * twice. Just to confuse this, we also sometimes just carry the + * fractional part in calculations, in both signed and unsigned forms. + * Anyway, an l_fp looks like: + * + * 0 1 2 3 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Integral Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fractional Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * REF http://www.eecis.udel.edu/~mills/database/rfc/rfc2030.txt + */ + + +typedef struct { + union { + uint32_t Xl_ui; + int32_t Xl_i; + } Ul_i; + union { + uint32_t Xl_uf; + int32_t Xl_f; + } Ul_f; +} l_fp; + + +inline void Ntp2Unix(uint32_t &n, time_t &u) +{ + // Ntp's time scale starts in 1900, Unix in 1970. + + u = n - 0x83aa7e80; // 2208988800 1970 - 1900 in seconds +} + +inline void HTONL_FP(l_fp *h, l_fp *n) +{ + (n)->Ul_i.Xl_ui = htonl((h)->Ul_i.Xl_ui); + (n)->Ul_f.Xl_uf = htonl((h)->Ul_f.Xl_uf); +} + +inline void ntohl_fp(l_fp *n, l_fp *h) +{ + (h)->Ul_i.Xl_ui = ntohl((n)->Ul_i.Xl_ui); + (h)->Ul_f.Xl_uf = ntohl((n)->Ul_f.Xl_uf); +} + +struct pkt { + uint8_t li_vn_mode; /* leap indicator, version and mode */ + uint8_t stratum; /* peer stratum */ + uint8_t ppoll; /* peer poll interval */ + int8_t precision; /* peer clock precision */ + uint32_t rootdelay; /* distance to primary clock */ + uint32_t rootdispersion; /* clock dispersion */ + uint32_t refid; /* reference clock ID */ + l_fp ref; /* time peer clock was last updated */ + l_fp org; /* originate time stamp */ + l_fp rec; /* receive time stamp */ + l_fp xmt; /* transmit time stamp */ + + uint32_t exten[1]; /* misused */ + uint8_t mac[5 * sizeof(uint32_t)]; /* mac */ +}; + +int nServersCount = 65; + +std::string NtpServers[65] = { + // Microsoft + "time.windows.com", + + // Google + "time1.google.com", + "time2.google.com", + + // Russian Federation + "ntp.ix.ru", + "0.ru.pool.ntp.org", + "1.ru.pool.ntp.org", + "2.ru.pool.ntp.org", + "3.ru.pool.ntp.org", + "ntp1.stratum2.ru", + "ntp2.stratum2.ru", + "ntp3.stratum2.ru", + "ntp4.stratum2.ru", + "ntp5.stratum2.ru", + "ntp6.stratum2.ru", + "ntp7.stratum2.ru", + "ntp1.stratum1.ru", + "ntp2.stratum1.ru", + "ntp3.stratum1.ru", + "ntp4.stratum1.ru", + "ntp1.vniiftri.ru", + "ntp2.vniiftri.ru", + "ntp3.vniiftri.ru", + "ntp4.vniiftri.ru", + "ntp21.vniiftri.ru", + "ntp1.niiftri.irkutsk.ru", + "ntp2.niiftri.irkutsk.ru", + "vniiftri.khv.ru", + "vniiftri2.khv.ru", + + // United States + "nist1-pa.ustiming.org", + "time-a.nist.gov ", + "time-b.nist.gov ", + "time-c.nist.gov ", + "time-d.nist.gov ", + "nist1-macon.macon.ga.us", + "nist.netservicesgroup.com", + "nisttime.carsoncity.k12.mi.us", + "nist1-lnk.binary.net", + "wwv.nist.gov", + "time-a.timefreq.bldrdoc.gov", + "time-b.timefreq.bldrdoc.gov", + "time-c.timefreq.bldrdoc.gov", + "time.nist.gov", + "utcnist.colorado.edu", + "utcnist2.colorado.edu", + "ntp-nist.ldsbc.net", + "nist1-lv.ustiming.org", + "time-nw.nist.gov", + "nist-time-server.eoni.com", + "nist-time-server.eoni.com", + "ntp1.bu.edu", + "ntp2.bu.edu", + "ntp3.bu.edu", + + // South Africa + "ntp1.meraka.csir.co.za", + "ntp.is.co.za", + "ntp2.is.co.za", + "igubu.saix.net", + "ntp1.neology.co.za", + "ntp2.neology.co.za", + "tick.meraka.csir.co.za", + "tock.meraka.csir.co.za", + "ntp.time.org.za", + "ntp1.meraka.csir.co.za", + "ntp2.meraka.csir.co.za", + + // Italy + "ntp1.inrim.it", + "ntp2.inrim.it", + + // ... To be continued +}; + +#ifdef WIN32 +bool InitWithRandom(int &sockfd, int &servlen, struct sockaddr *pcliaddr) +#else +bool InitWithRandom(int &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) +#endif +{ + int nAttempt = 0; + + while(nAttempt < 100) + { + sockfd = -1; + nAttempt++; + + int nServerNum = GetRandInt(nServersCount); + + std::vector vIP; + bool fRet = LookupHost(NtpServers[nServerNum].c_str(), vIP, 10, true); + if (!fRet) + continue; + + struct sockaddr_in servaddr; + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(123); + + bool found = false; + for(unsigned int i = 0; i < vIP.size(); i++) + { + if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) + { + break; + } + } + + if (!found) + continue; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd == -1) + continue; // socket initialization error + + if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) + { + continue; // "connection" error + } + + *pcliaddr = *((struct sockaddr *) &servaddr); + servlen = sizeof(servaddr); + return true; + } + + return false; +} + +#ifdef WIN32 +bool InitWithHost(std::string &strHostName, int &sockfd, int &servlen, struct sockaddr *pcliaddr) +#else +bool InitWithHost(std::string &strHostName, int &sockfd, socklen_t &servlen, struct sockaddr *pcliaddr) +#endif +{ + sockfd = -1; + + std::vector vIP; + bool fRet = LookupHost(strHostName.c_str(), vIP, 10, true); + if (!fRet) + return false; + + struct sockaddr_in servaddr; + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(123); + + bool found = false; + for(unsigned int i = 0; i < vIP.size(); i++) + { + if ((found = vIP[i].GetInAddr(&servaddr.sin_addr))) + { + break; + } + } + + if (!found) + return false; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd == -1) + return false; // socket initialization error + + if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1 ) + { + return false; // "connection" error + } + + *pcliaddr = *((struct sockaddr *) &servaddr); + servlen = sizeof(servaddr); + + return true; +} + + +#ifdef WIN32 +int64_t DoReq(int sockfd, int servlen, struct sockaddr cliaddr) +#else +int64_t DoReq(int sockfd, socklen_t servlen, struct sockaddr cliaddr) +#endif +{ + struct pkt *msg = new pkt; + struct pkt *prt = new pkt; + + msg->li_vn_mode=227; + msg->stratum=0; + msg->ppoll=4; + msg->precision=0; + msg->rootdelay=0; + msg->rootdispersion=0; + + msg->ref.Ul_i.Xl_i=0; + msg->ref.Ul_f.Xl_f=0; + msg->org.Ul_i.Xl_i=0; + msg->org.Ul_f.Xl_f=0; + msg->rec.Ul_i.Xl_i=0; + msg->rec.Ul_f.Xl_f=0; + msg->xmt.Ul_i.Xl_i=0; + msg->xmt.Ul_f.Xl_f=0; + + int len=48; + sendto(sockfd, (char *) msg, len, 0, &cliaddr, servlen); + int n = recvfrom(sockfd, msg, len, 0, NULL, NULL); + + ntohl_fp(&msg->rec, &prt->rec); + ntohl_fp(&msg->xmt, &prt->xmt); + + time_t seconds_receive; + time_t seconds_transmit; + + Ntp2Unix(prt->rec.Ul_i.Xl_ui, seconds_receive); + Ntp2Unix(prt->xmt.Ul_i.Xl_ui, seconds_transmit); + + delete msg; + delete prt; + + return (seconds_receive + seconds_transmit) / 2; +} + +int64_t NtpGetTime() +{ + int sockfd; + struct sockaddr cliaddr; + +#ifdef WIN32 + int servlen; +#else + socklen_t servlen; +#endif + + if (!InitWithRandom(sockfd, servlen, &cliaddr)) + return -1; + + int64_t nTime = DoReq(sockfd, servlen, cliaddr); + +#ifdef WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + + return nTime; +} + +int64_t NtpGetTime(std::string &strHostName) +{ + int sockfd; + struct sockaddr cliaddr; + +#ifdef WIN32 + int servlen; +#else + socklen_t servlen; +#endif + + if (!InitWithHost(strHostName, sockfd, servlen, &cliaddr)) + return -1; + + int64_t nTime = DoReq(sockfd, servlen, cliaddr); + +#ifdef WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif + + return nTime; +} diff --git a/src/ntp.h b/src/ntp.h new file mode 100644 index 0000000..4963124 --- /dev/null +++ b/src/ntp.h @@ -0,0 +1,3 @@ +int64_t NtpGetTime(); +int64_t NtpGetTime(std::string &strHostName); + diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index e67c5f1..d0f4516 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -8,6 +8,7 @@ #include "db.h" #include "walletdb.h" #include "net.h" +#include "ntp.h" using namespace json_spirit; using namespace std; @@ -344,4 +345,29 @@ Value getnettotals(const Array& params, bool fHelp) obj.push_back(Pair("totalbytessent", static_cast(CNode::GetTotalBytesSent()))); obj.push_back(Pair("timemillis", static_cast(GetTimeMillis()))); return obj; -} \ No newline at end of file +} + +Value ntptime(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "ntptime [ntpserver]\n" + "Returns current time from specific or random NTP server."); + + int64_t nTime; + if (params.size() > 0) + { + string strHostName = params[0].get_str(); + nTime = NtpGetTime(strHostName); + } + else + nTime = NtpGetTime(); + + if (nTime < 0) + throw runtime_error("Request error"); + + Object obj; + obj.push_back(Pair("epoch", nTime)); + obj.push_back(Pair("time", DateTimeStrFormat(nTime))); + return obj; +} -- 1.7.1