• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "sntp_client.h"
17 
18 #include <chrono>
19 #include <cstdio>
20 #include <ctime>
21 #include <iomanip>
22 #include <netdb.h>
23 #include <netinet/in.h>
24 #include <securec.h>
25 #include <sstream>
26 #include <string>
27 #include <sys/socket.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 
32 #include "time_common.h"
33 
34 namespace OHOS {
35 namespace MiscServices {
36 namespace {
37 constexpr uint64_t SECONDS_SINCE_FIRST_EPOCH = 2208988800; // Seconds from 1/1/1900 00.00 to 1/1/1970 00.00;
38 constexpr uint64_t MILLISECOND_TO_SECOND = 1000;
39 constexpr uint64_t FRACTION_TO_SECOND = 0x100000000;
40 constexpr uint64_t UINT32_MASK = 0xFFFFFFFF;
41 const int VERSION_MASK = 0x38;
42 const int MODE_MASK = 0x7;
43 constexpr int32_t INDEX_ZERO = 0;
44 constexpr int32_t INDEX_ONE = 1;
45 constexpr int32_t INDEX_TWO = 2;
46 constexpr int32_t INDEX_THREE = 3;
47 constexpr int32_t INDEX_FOUR = 4;
48 constexpr int32_t TIME_OUT = 5;
49 constexpr unsigned char MODE_THREE = 3;
50 constexpr unsigned char VERSION_THREE = 3;
51 constexpr double TEN_TO_MINUS_SIX_POWER = 1.0e-6;
52 char const *NTP_PORT = "123";
53 constexpr int32_t NTP_MSG_OFFSET_ROOT_DELAY = 4;
54 constexpr int32_t NTP_MSG_OFFSET_ROOT_DISPERSION = 8;
55 constexpr int32_t NTP_MSG_OFFSET_REFERENCE_IDENTIFIER = 12;
56 constexpr int32_t REFERENCE_TIMESTAMP_OFFSET = 16;
57 constexpr int32_t ORIGINATE_TIMESTAMP_OFFSET = 24;
58 constexpr int32_t RECEIVE_TIMESTAMP_OFFSET = 32;
59 constexpr int32_t TRANSMIT_TIMESTAMP_OFFSET = 40;
60 constexpr int32_t NTP_PACKAGE_SIZE = 48;
61 constexpr int32_t SNTP_MSG_OFFSET_SIX = 6;
62 constexpr int32_t SNTP_MSG_OFFSET_THREE = 3;
63 } // namespace
64 
RequestTime(const std::string & host)65 bool SNTPClient::RequestTime(const std::string &host)
66 {
67     int bufLen = NTP_PACKAGE_SIZE;
68     struct addrinfo hints = { 0 }, *addrs;
69     hints.ai_family = AF_INET;
70     hints.ai_socktype = SOCK_DGRAM;
71     hints.ai_protocol = IPPROTO_UDP;
72     int error = getaddrinfo(host.c_str(), NTP_PORT, &hints, &addrs);
73     if (error != 0) {
74         TIME_HILOGE(TIME_MODULE_SERVICE, "getaddrinfo failed error %{public}d", error);
75         return false;
76     }
77 
78     // Create a socket for sending data
79     int sendSocket = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
80     if (sendSocket < 0) {
81         TIME_HILOGE(TIME_MODULE_SERVICE,
82                     "create socket failed: %{public}s family: %{public}d socktype: %{public}d protocol: %{public}d",
83                     strerror(errno), addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
84         return false;
85     }
86 
87     // Set send and recv function timeout
88     struct timeval timeout = { TIME_OUT, 0 };
89     setsockopt(sendSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
90     setsockopt(sendSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
91     if (connect(sendSocket, addrs->ai_addr, addrs->ai_addrlen) < 0) {
92         TIME_HILOGE(TIME_MODULE_SERVICE, "socket connect failed: %{public}s", strerror(errno));
93         close(sendSocket);
94         return false;
95     }
96 
97     // Create the NTP tx timestamp and fill the fields in the msg to be tx
98     char sendBuf[NTP_PACKAGE_SIZE] = { 0 };
99     CreateMessage(sendBuf);
100     if (send(sendSocket, sendBuf, bufLen, 0) < 0) {
101         TIME_HILOGE(TIME_MODULE_SERVICE, "Send socket message failed: %{public}s, Host: %{public}s",
102                     strerror(errno), host.c_str());
103         close(sendSocket);
104         return false;
105     }
106 
107     char bufferRx[NTP_PACKAGE_SIZE] = { 0 };
108     // Receive until the peer closes the connection
109     if (recv(sendSocket, bufferRx, NTP_PACKAGE_SIZE, 0) < 0) {
110         TIME_HILOGE(TIME_MODULE_SERVICE, "Receive socket message failed: %{public}s, Host: %{public}s",
111                     strerror(errno), host.c_str());
112         close(sendSocket);
113         return false;
114     }
115     ReceivedMessage(bufferRx);
116     close(sendSocket);
117     return true;
118 }
119 
SetClockOffset(int clockOffset)120 void SNTPClient::SetClockOffset(int clockOffset)
121 {
122     m_clockOffset = clockOffset;
123 }
124 
GetNtpTimestamp64(int offset,const char * buffer)125 uint64_t SNTPClient::GetNtpTimestamp64(int offset, const char *buffer)
126 {
127     TIME_HILOGD(TIME_MODULE_SERVICE, "start.");
128     const int _len = sizeof(uint64_t);
129     char valueRx[_len];
130     errno_t ret = memset_s(valueRx, sizeof(uint64_t), 0, sizeof(uint64_t));
131     if (ret != EOK) {
132         TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
133         return false;
134     }
135     int numOfBit = sizeof(uint64_t) - 1;
136     for (int loop = offset; loop < offset + _len; loop++) {
137         valueRx[numOfBit] = buffer[loop];
138         numOfBit--;
139     }
140 
141     uint64_t milliseconds;
142     ret = memcpy_s(&milliseconds, sizeof(uint64_t), valueRx, sizeof(uint64_t));
143     if (ret != EOK) {
144         TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
145         return false;
146     }
147     return le64toh(milliseconds);
148 }
149 
ConvertUnixToNtp(struct ntp_timestamp * ntpTs,struct timeval * unixTs)150 void SNTPClient::ConvertUnixToNtp(struct ntp_timestamp *ntpTs, struct timeval *unixTs)
151 {
152     TIME_HILOGD(TIME_MODULE_SERVICE, "start.");
153     // 0x83AA7E80; the seconds from Jan 1, 1900 to Jan 1, 1970
154     ntpTs->second = unixTs->tv_sec + SECONDS_SINCE_FIRST_EPOCH; // 0x83AA7E80;
155     ntpTs->fraction =
156         static_cast<uint64_t>((unixTs->tv_usec + 1) * (1LL << RECEIVE_TIMESTAMP_OFFSET) * TEN_TO_MINUS_SIX_POWER);
157     TIME_HILOGD(TIME_MODULE_SERVICE, "end.");
158 }
159 
160 /*
161   *	/// SNTP Timestamp Format (as described in RFC 2030)
162   *                         1                   2                   3
163   *   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
164   *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
165   *   |                           Seconds                             |
166   *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
167   *   |                  Seconds Fraction (0-padded)                  |
168   *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
169   */
ConvertNtpToStamp(uint64_t _ntpTs)170 int64_t SNTPClient::ConvertNtpToStamp(uint64_t _ntpTs)
171 {
172     auto second = static_cast<uint32_t>((_ntpTs >> RECEIVE_TIMESTAMP_OFFSET) & UINT32_MASK);
173     auto fraction = static_cast<uint32_t>(_ntpTs & UINT32_MASK);
174     if (second == 0 && fraction == 0) {
175         return 0;
176     }
177     if (second < SECONDS_SINCE_FIRST_EPOCH) {
178         return 0;
179     }
180     // convert sntp timestamp to seconds
181     return ((second - SECONDS_SINCE_FIRST_EPOCH) * MILLISECOND_TO_SECOND) +
182            ((fraction * MILLISECOND_TO_SECOND) / FRACTION_TO_SECOND);
183 }
184 
CreateMessage(char * buffer)185 void SNTPClient::CreateMessage(char *buffer)
186 {
187     TIME_HILOGD(TIME_MODULE_SERVICE, "start.");
188     struct ntp_timestamp ntp{};
189     struct timeval unix;
190 
191     gettimeofday(&unix, nullptr);
192     // convert unix time to ntp time
193     ConvertUnixToNtp(&ntp, &unix);
194     uint64_t _ntpTs = ntp.second;
195     _ntpTs = (_ntpTs << RECEIVE_TIMESTAMP_OFFSET) | ntp.fraction;
196     m_originateTimestamp = _ntpTs;
197 
198     SNTPMessage _sntpMsg{};
199     // Important, if you don't set the version/mode, the server will ignore you.
200     _sntpMsg.clear();
201     _sntpMsg._leapIndicator = 0;
202     _sntpMsg._versionNumber = VERSION_THREE;
203     _sntpMsg._mode = MODE_THREE;
204     // optional (?)
205     _sntpMsg._originateTimestamp = _ntpTs;
206     char value[sizeof(uint64_t)];
207     errno_t ret = memcpy_s(value, sizeof(uint64_t), &_sntpMsg._originateTimestamp, sizeof(uint64_t));
208     if (ret != EOK) {
209         TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
210         return;
211     }
212     int numOfBit = sizeof(uint64_t) - 1;
213     int offsetEnd = ORIGINATE_TIMESTAMP_OFFSET + sizeof(uint64_t);
214     for (int loop = ORIGINATE_TIMESTAMP_OFFSET; loop < offsetEnd; loop++) {
215         buffer[loop] = value[numOfBit];
216         numOfBit--;
217     }
218     // create the 1-byte info in one go... the result should be 27 :)
219     buffer[INDEX_ZERO] = (_sntpMsg._leapIndicator << SNTP_MSG_OFFSET_SIX) |
220                          (_sntpMsg._versionNumber << SNTP_MSG_OFFSET_THREE) | _sntpMsg._mode;
221     TIME_HILOGD(TIME_MODULE_SERVICE, "end.");
222 }
223 
ReceivedMessage(char * buffer)224 void SNTPClient::ReceivedMessage(char *buffer)
225 {
226     struct ntp_timestamp ntp{};
227     struct timeval unix;
228     gettimeofday(&unix, NULL);
229     ConvertUnixToNtp(&ntp, &unix);
230     uint64_t _ntpTs = ntp.second;
231     _ntpTs = (_ntpTs << RECEIVE_TIMESTAMP_OFFSET) | ntp.fraction;
232     SNTPMessage _sntpMsg;
233     _sntpMsg.clear();
234     _sntpMsg._leapIndicator = buffer[INDEX_ZERO] >> SNTP_MSG_OFFSET_SIX;
235     _sntpMsg._versionNumber = (buffer[INDEX_ZERO] & VERSION_MASK) >> SNTP_MSG_OFFSET_THREE;
236     _sntpMsg._mode = (buffer[INDEX_ZERO] & MODE_MASK);
237     _sntpMsg._stratum = buffer[INDEX_ONE];
238     _sntpMsg._pollInterval = buffer[INDEX_TWO];
239     _sntpMsg._precision = buffer[INDEX_THREE];
240     _sntpMsg._rootDelay = GetNtpField32(NTP_MSG_OFFSET_ROOT_DELAY, buffer);
241     _sntpMsg._rootDispersion = GetNtpField32(NTP_MSG_OFFSET_ROOT_DISPERSION, buffer);
242     int _refId[INDEX_FOUR];
243     GetReferenceId(NTP_MSG_OFFSET_REFERENCE_IDENTIFIER, buffer, _refId);
244     _sntpMsg._referenceIdentifier[INDEX_ZERO] = _refId[INDEX_ZERO];
245     _sntpMsg._referenceIdentifier[INDEX_ONE] = _refId[INDEX_ONE];
246     _sntpMsg._referenceIdentifier[INDEX_TWO] = _refId[INDEX_TWO];
247     _sntpMsg._referenceIdentifier[INDEX_THREE] = _refId[INDEX_THREE];
248     _sntpMsg._referenceTimestamp = GetNtpTimestamp64(REFERENCE_TIMESTAMP_OFFSET, buffer);
249     _sntpMsg._originateTimestamp = GetNtpTimestamp64(ORIGINATE_TIMESTAMP_OFFSET, buffer);
250     _sntpMsg._receiveTimestamp = GetNtpTimestamp64(RECEIVE_TIMESTAMP_OFFSET, buffer);
251     _sntpMsg._transmitTimestamp = GetNtpTimestamp64(TRANSMIT_TIMESTAMP_OFFSET, buffer);
252     uint64_t _tempOriginate = m_originateTimestamp;
253     if (_sntpMsg._originateTimestamp > 0) {
254         _tempOriginate = _sntpMsg._originateTimestamp;
255     }
256     int64_t _originClient = ConvertNtpToStamp(_tempOriginate);
257     int64_t _receiveServer = ConvertNtpToStamp(_sntpMsg._receiveTimestamp);
258     int64_t _transmitServer = ConvertNtpToStamp(_sntpMsg._transmitTimestamp);
259     int64_t _receiveClient = ConvertNtpToStamp(_ntpTs);
260     int64_t _clockOffset = (((_receiveServer - _originClient) + (_transmitServer - _receiveClient)) / INDEX_TWO);
261     int64_t _roundTripDelay = (_receiveClient - _originClient) - (_transmitServer - _receiveServer);
262     mRoundTripTime = _roundTripDelay;
263     mNtpTime = ConvertNtpToStamp(_ntpTs) + _clockOffset;
264     mNtpTimeReference =
265         std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch())
266             .count();
267     SetClockOffset(_clockOffset);
268     TIME_HILOGI(TIME_MODULE_SERVICE, "_originClient:%{public}s, _receiveServer:%{public}s, _transmitServer:%{public}s,"
269                 "_receiveClient:%{public}s", std::to_string(_originClient).c_str(),
270                 std::to_string(_receiveServer).c_str(), std::to_string(_transmitServer).c_str(),
271                 std::to_string(_receiveClient).c_str());
272 }
273 
GetNtpField32(int offset,const char * buffer)274 unsigned int SNTPClient::GetNtpField32(int offset, const char *buffer)
275 {
276     TIME_HILOGD(TIME_MODULE_SERVICE, "start.");
277     const int _len = sizeof(int);
278     char valueRx[_len];
279     errno_t ret = memset_s(valueRx, _len, 0, _len);
280     if (ret != EOK) {
281         TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
282         return false;
283     }
284     int numOfBit = sizeof(int) - 1;
285     for (int loop = offset; loop < offset + _len; loop++) {
286         valueRx[numOfBit] = buffer[loop];
287         numOfBit--;
288     }
289 
290     unsigned int milliseconds;
291     errno_t retValue = memcpy_s(&milliseconds, sizeof(int), valueRx, sizeof(int));
292     if (retValue != EOK) {
293         TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", retValue);
294         return false;
295     }
296     TIME_HILOGD(TIME_MODULE_SERVICE, "end.");
297     return milliseconds;
298 }
299 
GetReferenceId(int offset,char * buffer,int * _outArray)300 void SNTPClient::GetReferenceId(int offset, char *buffer, int *_outArray)
301 {
302     TIME_HILOGD(TIME_MODULE_SERVICE, "start.");
303     const int _len = sizeof(int);
304     int num = 0;
305     for (int loop = offset; loop < offset + _len; loop++) {
306         _outArray[num] = buffer[loop];
307         num++;
308     }
309     TIME_HILOGD(TIME_MODULE_SERVICE, "end.");
310 }
311 
clear()312 void SNTPClient::SNTPMessage::clear()
313 {
314     TIME_HILOGD(TIME_MODULE_SERVICE, "start.");
315     errno_t ret = memset_s(this, sizeof(*this), 0, sizeof(*this));
316     if (ret != EOK) {
317         TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
318     }
319 }
320 
getNtpTime()321 int64_t SNTPClient::getNtpTime()
322 {
323     return mNtpTime;
324 }
325 
getNtpTimeReference()326 int64_t SNTPClient::getNtpTimeReference()
327 {
328     return mNtpTimeReference;
329 }
330 
getRoundTripTime()331 int64_t SNTPClient::getRoundTripTime()
332 {
333     return mRoundTripTime;
334 }
335 } // namespace MiscServices
336 } // namespace OHOS