* Copyright (C) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sntp_client.h"
#include "ntp_trusted_time.h"
#include <netdb.h>
#include <securec.h>
#include <sstream>
#include <sys/time.h>
#include "time_sysevent.h"
namespace OHOS {
namespace MiscServices {
namespace {
constexpr uint64_t SECONDS_SINCE_FIRST_EPOCH = 2208988800;
constexpr uint64_t MILLISECOND_TO_SECOND = 1000;
constexpr uint64_t FRACTION_TO_SECOND = 0x100000000;
constexpr uint64_t UINT32_MASK = 0xFFFFFFFF;
constexpr int VERSION_MASK = 0x38;
constexpr int MODE_MASK = 0x7;
constexpr int32_t INDEX_ZERO = 0;
constexpr int32_t INDEX_ONE = 1;
constexpr int32_t INDEX_TWO = 2;
constexpr int32_t INDEX_THREE = 3;
constexpr int32_t INDEX_FOUR = 4;
constexpr int32_t TIME_OUT = 5;
constexpr unsigned char MODE_THREE = 3;
constexpr unsigned char VERSION_THREE = 3;
constexpr double TEN_TO_MINUS_SIX_POWER = 1.0e-6;
constexpr const char* NTP_PORT = "123";
constexpr int32_t NTP_MSG_OFFSET_ROOT_DELAY = 4;
constexpr int32_t NTP_MSG_OFFSET_ROOT_DISPERSION = 8;
constexpr int32_t NTP_MSG_OFFSET_REFERENCE_IDENTIFIER = 12;
constexpr int32_t REFERENCE_TIMESTAMP_OFFSET = 16;
constexpr int32_t ORIGINATE_TIMESTAMP_OFFSET = 24;
constexpr int32_t RECEIVE_TIMESTAMP_OFFSET = 32;
constexpr int32_t TRANSMIT_TIMESTAMP_OFFSET = 40;
constexpr int32_t NTP_PACKAGE_SIZE = 48;
constexpr int32_t SNTP_MSG_OFFSET_SIX = 6;
constexpr int32_t SNTP_MSG_OFFSET_THREE = 3;
}
bool SNTPClient::RequestTime(const std::string &host)
{
int bufLen = NTP_PACKAGE_SIZE;
struct addrinfo hints = { 0 };
struct addrinfo *addrs;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
int error = getaddrinfo(host.c_str(), NTP_PORT, &hints, &addrs);
if (error != 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "getaddrinfo failed error %{public}d", error);
return false;
}
int sendSocket = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (sendSocket < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE,
"create socket failed: %{public}s family: %{public}d socktype: %{public}d protocol: %{public}d",
strerror(errno), addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
return false;
}
fdsan_exchange_owner_tag(sendSocket, 0, BASE_TIME_FDSAN_TAG);
struct timeval timeout = { TIME_OUT, 0 };
setsockopt(sendSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
setsockopt(sendSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
if (connect(sendSocket, addrs->ai_addr, addrs->ai_addrlen) < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "socket connect failed: %{public}s", strerror(errno));
fdsan_close_with_tag(sendSocket, BASE_TIME_FDSAN_TAG);
return false;
}
char sendBuf[NTP_PACKAGE_SIZE] = { 0 };
CreateMessage(sendBuf);
if (send(sendSocket, sendBuf, bufLen, 0) < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "Send socket message failed: %{public}s, Host: %{public}s",
strerror(errno), host.c_str());
fdsan_close_with_tag(sendSocket, BASE_TIME_FDSAN_TAG);
return false;
}
char bufferRx[NTP_PACKAGE_SIZE] = { 0 };
if (recv(sendSocket, bufferRx, NTP_PACKAGE_SIZE, 0) < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "Receive socket message failed: %{public}s, Host: %{public}s",
strerror(errno), host.c_str());
fdsan_close_with_tag(sendSocket, BASE_TIME_FDSAN_TAG);
return false;
}
fdsan_close_with_tag(sendSocket, BASE_TIME_FDSAN_TAG);
if (!ReceivedMessage(bufferRx)) {
TIME_HILOGE(TIME_MODULE_SERVICE, "ReceivedMessage failed: Host: %{public}s", host.c_str());
return false;
}
return true;
}
void SNTPClient::SetClockOffset(int clockOffset)
{
m_clockOffset = clockOffset;
}
uint64_t SNTPClient::GetNtpTimestamp64(int offset, const char *buffer)
{
const int _len = sizeof(uint64_t);
char valueRx[_len];
errno_t ret = memset_s(valueRx, sizeof(uint64_t), 0, sizeof(uint64_t));
if (ret != EOK) {
TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
return false;
}
int numOfBit = sizeof(uint64_t) - 1;
for (int loop = offset; loop < offset + _len; loop++) {
valueRx[numOfBit] = buffer[loop];
numOfBit--;
}
uint64_t milliseconds;
ret = memcpy_s(&milliseconds, sizeof(uint64_t), valueRx, sizeof(uint64_t));
if (ret != EOK) {
TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
return false;
}
return le64toh(milliseconds);
}
void SNTPClient::ConvertUnixToNtp(struct ntp_timestamp *ntpTs, struct timeval *unixTs)
{
ntpTs->second = unixTs->tv_sec + SECONDS_SINCE_FIRST_EPOCH;
ntpTs->fraction =
static_cast<uint64_t>((unixTs->tv_usec + 1) * (1LL << RECEIVE_TIMESTAMP_OFFSET) * TEN_TO_MINUS_SIX_POWER);
TIME_HILOGD(TIME_MODULE_SERVICE, "end");
}
* /// SNTP Timestamp Format (as described in RFC 2030)
* 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Seconds |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Seconds Fraction (0-padded) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
int64_t SNTPClient::ConvertNtpToStamp(uint64_t _ntpTs)
{
auto second = static_cast<uint32_t>((_ntpTs >> RECEIVE_TIMESTAMP_OFFSET) & UINT32_MASK);
auto fraction = static_cast<uint32_t>(_ntpTs & UINT32_MASK);
if (second == 0 && fraction == 0) {
return 0;
}
if (second < SECONDS_SINCE_FIRST_EPOCH) {
return 0;
}
return ((second - SECONDS_SINCE_FIRST_EPOCH) * MILLISECOND_TO_SECOND) +
((fraction * MILLISECOND_TO_SECOND) / FRACTION_TO_SECOND);
}
void SNTPClient::CreateMessage(char *buffer)
{
struct ntp_timestamp ntp{};
struct timeval unix;
gettimeofday(&unix, nullptr);
ConvertUnixToNtp(&ntp, &unix);
uint64_t _ntpTs = ntp.second;
_ntpTs = (_ntpTs << RECEIVE_TIMESTAMP_OFFSET) | ntp.fraction;
errno_t ret = TimeUtils::GetBootTimeMs(m_originateTimestamp);
if (ret != E_TIME_OK) {
return;
}
SNTPMessage _sntpMsg{};
_sntpMsg.clear();
_sntpMsg._leapIndicator = 0;
_sntpMsg._versionNumber = VERSION_THREE;
_sntpMsg._mode = MODE_THREE;
_sntpMsg._originateTimestamp = _ntpTs;
char value[sizeof(uint64_t)];
ret = memcpy_s(value, sizeof(uint64_t), &_sntpMsg._originateTimestamp, sizeof(uint64_t));
if (ret != EOK) {
TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
return;
}
int numOfBit = sizeof(uint64_t) - 1;
int offsetEnd = ORIGINATE_TIMESTAMP_OFFSET + sizeof(uint64_t);
for (int loop = ORIGINATE_TIMESTAMP_OFFSET; loop < offsetEnd; loop++) {
buffer[loop] = value[numOfBit];
numOfBit--;
}
buffer[INDEX_ZERO] = (_sntpMsg._leapIndicator << SNTP_MSG_OFFSET_SIX) |
(_sntpMsg._versionNumber << SNTP_MSG_OFFSET_THREE) | _sntpMsg._mode;
TIME_HILOGD(TIME_MODULE_SERVICE, "end");
}
bool SNTPClient::ReceivedMessage(char *buffer)
{
int64_t receiveBootTime = 0;
errno_t ret = TimeUtils::GetBootTimeMs(receiveBootTime);
if (ret != E_TIME_OK) {
return false;
}
SNTPMessage _sntpMsg;
_sntpMsg.clear();
_sntpMsg._leapIndicator = buffer[INDEX_ZERO] >> SNTP_MSG_OFFSET_SIX;
_sntpMsg._versionNumber = (buffer[INDEX_ZERO] & VERSION_MASK) >> SNTP_MSG_OFFSET_THREE;
_sntpMsg._mode = (buffer[INDEX_ZERO] & MODE_MASK);
_sntpMsg._stratum = buffer[INDEX_ONE];
_sntpMsg._pollInterval = buffer[INDEX_TWO];
_sntpMsg._precision = buffer[INDEX_THREE];
_sntpMsg._rootDelay = GetNtpField32(NTP_MSG_OFFSET_ROOT_DELAY, buffer);
_sntpMsg._rootDispersion = GetNtpField32(NTP_MSG_OFFSET_ROOT_DISPERSION, buffer);
int _refId[INDEX_FOUR];
GetReferenceId(NTP_MSG_OFFSET_REFERENCE_IDENTIFIER, buffer, _refId);
_sntpMsg._referenceIdentifier[INDEX_ZERO] = _refId[INDEX_ZERO];
_sntpMsg._referenceIdentifier[INDEX_ONE] = _refId[INDEX_ONE];
_sntpMsg._referenceIdentifier[INDEX_TWO] = _refId[INDEX_TWO];
_sntpMsg._referenceIdentifier[INDEX_THREE] = _refId[INDEX_THREE];
_sntpMsg._referenceTimestamp = GetNtpTimestamp64(REFERENCE_TIMESTAMP_OFFSET, buffer);
_sntpMsg._originateTimestamp = GetNtpTimestamp64(ORIGINATE_TIMESTAMP_OFFSET, buffer);
_sntpMsg._receiveTimestamp = GetNtpTimestamp64(RECEIVE_TIMESTAMP_OFFSET, buffer);
_sntpMsg._transmitTimestamp = GetNtpTimestamp64(TRANSMIT_TIMESTAMP_OFFSET, buffer);
int64_t _originClient = m_originateTimestamp;
int64_t _receiveServer = ConvertNtpToStamp(_sntpMsg._receiveTimestamp);
int64_t _transmitServer = ConvertNtpToStamp(_sntpMsg._transmitTimestamp);
if (_transmitServer == 0 || _receiveServer == 0) {
return false;
}
int64_t _receiveClient = receiveBootTime;
int64_t _clockOffset = (((_receiveServer - _originClient) + (_transmitServer - _receiveClient)) / INDEX_TWO);
int64_t _roundTripDelay = (_receiveClient - _originClient) - (_transmitServer - _receiveServer);
mRoundTripTime = _roundTripDelay;
mNtpTime = receiveBootTime + _clockOffset;
TimeUtils::GetBootTimeMs(mNtpTimeReference);
SetClockOffset(_clockOffset);
TIME_HILOGI(TIME_MODULE_SERVICE, "_originClient:%{public}s, _receiveServer:%{public}s, _transmitServer:%{public}s,"
"_receiveClient:%{public}s", std::to_string(_originClient).c_str(),
std::to_string(_receiveServer).c_str(), std::to_string(_transmitServer).c_str(),
std::to_string(_receiveClient).c_str());
TimeBehaviorReport(ReportEventCode::NTP_REFRESH,
std::to_string(_originClient) + "|" + std::to_string(_receiveClient),
std::to_string(_transmitServer) + "|" + std::to_string(_receiveServer), mNtpTime);
return true;
}
unsigned int SNTPClient::GetNtpField32(int offset, const char *buffer)
{
const int _len = sizeof(int);
char valueRx[_len];
errno_t ret = memset_s(valueRx, _len, 0, _len);
if (ret != EOK) {
TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
return false;
}
int numOfBit = sizeof(int) - 1;
for (int loop = offset; loop < offset + _len; loop++) {
valueRx[numOfBit] = buffer[loop];
numOfBit--;
}
unsigned int milliseconds;
errno_t retValue = memcpy_s(&milliseconds, sizeof(int), valueRx, sizeof(int));
if (retValue != EOK) {
TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", retValue);
return false;
}
TIME_HILOGD(TIME_MODULE_SERVICE, "end");
return milliseconds;
}
void SNTPClient::GetReferenceId(int offset, char *buffer, int *_outArray)
{
const int _len = sizeof(int);
int num = 0;
for (int loop = offset; loop < offset + _len; loop++) {
_outArray[num] = buffer[loop];
num++;
}
TIME_HILOGD(TIME_MODULE_SERVICE, "end");
}
void SNTPClient::SNTPMessage::clear()
{
errno_t ret = memset_s(this, sizeof(*this), 0, sizeof(*this));
if (ret != EOK) {
TIME_HILOGE(TIME_MODULE_SERVICE, "memcpy_s failed, err = %{public}d", ret);
}
}
int64_t SNTPClient::getNtpTime()
{
return mNtpTime;
}
int64_t SNTPClient::getNtpTimeReference()
{
return mNtpTimeReference;
}
int64_t SNTPClient::getRoundTripTime()
{
return mRoundTripTime;
}
}
}