#include #include #include #include #include #include #include #include #include #include "core_sntp_client.h" /* @[code_example_sntpdnsresolve] */ /* Example POSIX implementation of SntpDnsReolve_t interface. */ static bool resolveDns( const SntpServerInfo_t * pServerAddr, uint32_t * pIpV4Addr ) { bool status = false; int32_t dnsStatus = -1; struct addrinfo hints; struct addrinfo * pListHead = NULL; hints.ai_family = AF_UNSPEC; hints.ai_socktype = ( int32_t ) SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; dnsStatus = getaddrinfo( pServerAddr->pServerName, NULL, &hints, &pListHead ); if( dnsStatus == 0 ) { struct sockaddr_in * pAddrInfo = ( struct sockaddr_in * ) pListHead->ai_addr; inet_ntop( pAddrInfo->sin_family, &pAddrInfo->sin_addr, ( int8_t * ) pIpV4Addr, INET_ADDRSTRLEN ); status = true; } freeaddrinfo( pListHead ); return status; } /* @[code_example_sntpdnsresolve] */ /* @[code_example_networkcontext] */ /* Example definition of NetworkContext_t for UDP socket operations. */ struct NetworkContext { int udpSocket; }; /* @[code_example_networkcontext] */ /* @[code_example_udptransport_sendto] */ /* Example POSIX implementation of the UdpTransportSendTo_t function of UDP transport interface. */ static int32_t UdpTransport_Send( NetworkContext_t * pNetworkContext, uint32_t serverAddr, uint16_t serverPort, const void * pBuffer, uint16_t bytesToSend ) { int32_t bytesSent = -1, pollStatus = 1; struct pollfd pollFds; pollFds.events = POLLOUT | POLLPRI; pollFds.revents = 0; pollFds.fd = pNetworkContext->udpSocket; /* Check if there is data to read from the socket. */ pollStatus = poll( &pollFds, 1, 0 ); if( pollStatus > 0 ) { struct sockaddr_in addrInfo; addrInfo.sin_family = AF_INET; addrInfo.sin_port = htons( serverPort ); addrInfo.sin_addr.s_addr = htonl( serverAddr ); bytesSent = sendto( pNetworkContext->udpSocket, pBuffer, bytesToSend, 0, ( const struct sockaddr * ) &addrInfo, sizeof( addrInfo ) ); } else if( pollStatus == 0 ) { bytesSent = 0; } return bytesSent; } /* @[code_example_udptransport_sendto] */ /* @[code_example_udptransport_recvfrom] */ /* Example POSIX implementation of the UdpTransportRecvFrom_t function of UDP transport interface. */ static int32_t UdpTransport_Recv( NetworkContext_t * pNetworkContext, uint32_t serverAddr, uint16_t serverPort, void * pBuffer, uint16_t bytesToRecv ) { int32_t bytesReceived = -1, pollStatus = 1; struct pollfd pollFds; pollFds.events = POLLIN | POLLPRI; pollFds.revents = 0; pollFds.fd = pNetworkContext->udpSocket; /* Check if there is data to read from the socket. */ pollStatus = poll( &pollFds, 1, 0 ); if( pollStatus > 0 ) { struct sockaddr_in addrInfo; addrInfo.sin_family = AF_INET; addrInfo.sin_port = htons( serverPort ); addrInfo.sin_addr.s_addr = htonl( serverAddr ); socklen_t addrLen = sizeof( addrInfo ); bytesReceived = recvfrom( pNetworkContext->udpSocket, pBuffer, bytesToRecv, 0, ( struct sockaddr * ) &addrInfo, &addrLen ); } else if( pollStatus == 0 ) { bytesReceived = 0; } return bytesReceived; } /* @[code_example_udptransport_recvfrom] */ /* @[code_example_sntpsettime] */ /* Example implementation of the SntpSetTime_t interface for POSIX platforms. */ static void sntpClient_SetTime( const SntpServerInfo_t * pTimeServer, const SntpTimestamp_t * pServerTime, int64_t clockOffsetMs, SntpLeapSecondInfo_t leapSecondInfo ) { /* @[code_example_sntp_converttounixtime] */ uint32_t unixSecs; uint32_t unixMs; SntpStatus_t status = Sntp_ConvertToUnixTime( pServerTime, &unixSecs, &unixMs ); /* @[code_example_sntp_converttounixtime] */ assert( status == SntpSuccess ); struct timespec serverTime = { .tv_sec = unixSecs, .tv_nsec = unixMs * 1000 }; clock_settime( CLOCK_REALTIME, &serverTime ); } /* @[code_example_sntpsettime] */ /* @[code_example_sntpgettime] */ /* Example implementation of the SntpGetTime_t interface for POSIX platforms. */ static void sntpClient_GetTime( SntpTimestamp_t * pCurrentTime ) { struct timespec currTime; ( void ) clock_gettime( CLOCK_REALTIME, &currTime ); pCurrentTime->seconds = currTime.tv_sec; pCurrentTime->fractions = ( currTime.tv_sec / 1000 ) * SNTP_FRACTION_VALUE_PER_MICROSECOND; } /* @[code_example_sntpgettime] */ /* Configuration constants for the example SNTP client. */ /* Following Time Servers are used for illustrating the usage of library API. * The library can be configured to use ANY time server, whether publicly available * time service like NTP Pool or a privately owned NTP server. */ #define TEST_TIME_SERVER_1 "0.pool.ntp.org" #define TEST_TIME_SERVER_2 "1.pool.ntp.org" #define SERVER_RESPONSE_TIMEOUT_MS 3000 #define TIME_REQUEST_SEND_WAIT_TIME_MS 2000 #define TIME_REQUEST_RECEIVE_WAIT_TIME_MS 1000 #define SYSTEM_CLOCK_FREQUENCY_TOLERANCE_PPM 500 #define SYSTEM_CLOCK_DESIRED_ACCURACY_MS 300 int main( void ) { /* @[code_example_sntp_init] */ /* Memory for network buffer. */ uint8_t networkBuffer[ SNTP_PACKET_BASE_SIZE ]; /* Create UDP socket. */ NetworkContext_t udpContext; udpContext.udpSocket = socket( AF_INET, SOCK_DGRAM, 0 ); /* Setup list of time servers. */ SntpServerInfo_t pTimeServers[] = { { .port = SNTP_DEFAULT_SERVER_PORT, .pServerName = TEST_TIME_SERVER_1, .serverNameLen = strlen( TEST_TIME_SERVER_1 ) }, { .port = SNTP_DEFAULT_SERVER_PORT, .pServerName = TEST_TIME_SERVER_2, .serverNameLen = strlen( TEST_TIME_SERVER_2 ) } }; /* Set the UDP transport interface object. */ UdpTransportInterface_t udpTransportIntf; udpTransportIntf.pUserContext = &udpContext; udpTransportIntf.sendTo = UdpTransport_Send; udpTransportIntf.recvFrom = UdpTransport_Recv; /* Context variable. */ SntpContext_t context; /* Initialize context. */ SntpStatus_t status = Sntp_Init( &context, pTimeServers, sizeof( pTimeServers ) / sizeof( SntpServerInfo_t ), SERVER_RESPONSE_TIMEOUT_MS, networkBuffer, SNTP_PACKET_BASE_SIZE, resolveDns, sntpClient_GetTime, sntpClient_SetTime, &udpTransportIntf, NULL ); assert( status == SntpSuccess ); /* @[code_example_sntp_init] */ /* Calculate the polling interval period for the SNTP client. */ /* @[code_example_sntp_calculatepollinterval] */ uint32_t pollingIntervalPeriod; status = Sntp_CalculatePollInterval( SYSTEM_CLOCK_FREQUENCY_TOLERANCE_PPM, SYSTEM_CLOCK_DESIRED_ACCURACY_MS, &pollingIntervalPeriod ); /* @[code_example_sntp_calculatepollinterval] */ assert( status == SntpSuccess ); /* Loop of SNTP client for period time synchronization. */ /* @[code_example_sntp_send_receive] */ while( 1 ) { status = Sntp_SendTimeRequest( &context, rand() % UINT32_MAX, TIME_REQUEST_SEND_WAIT_TIME_MS ); assert( status == SntpSuccess ); do { status = Sntp_ReceiveTimeResponse( &context, TIME_REQUEST_RECEIVE_WAIT_TIME_MS ); } while( status == SntpNoResponseReceived ); assert( status == SntpSuccess ); /* Delay of poll interval period before next time synchronization. */ sleep( pollingIntervalPeriod ); } /* @[code_example_sntp_send_receive] */ return EXIT_SUCCESS; }