153 lines
4.1 KiB
C
153 lines
4.1 KiB
C
// NTP client simple enough to print the current date and time in ctime() and ISO 8601 format
|
|
// https://datatracker.ietf.org/doc/html/rfc5905
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#define NTP_PORT 123
|
|
#define _INT2STR(x) #x
|
|
#define MACRO_INT2STR(x) _INT2STR(x)
|
|
#define NTP_PORT_STR MACRO_INT2STR(NTP_PORT)
|
|
//const uint8_t NTP_PORT = 123;
|
|
const uint64_t NTP_UNIX_DELTA = 2208988800ull; // January 1, 1970 in NTP timestamp format
|
|
const uint8_t NTP_CURRENT_VER = 4;
|
|
|
|
struct ntp_packet
|
|
{
|
|
uint8_t leap_ver_mode;
|
|
uint8_t stratum;
|
|
uint8_t poll;
|
|
uint8_t precision;
|
|
|
|
uint32_t root_delay;
|
|
uint32_t root_dispersion;
|
|
uint32_t ref_id;
|
|
|
|
uint32_t ref_tstamp_sec;
|
|
uint32_t ref_tstamp_frac;
|
|
|
|
uint32_t origin_tstamp_sec;
|
|
uint32_t origin_tstamp_frac;
|
|
|
|
uint32_t rx_tstamp_sec;
|
|
uint32_t rx_tstamp_frac;
|
|
|
|
uint32_t tx_tstamp_sec;
|
|
uint32_t tx_tstamp_frac;
|
|
};
|
|
|
|
//#include <sys/time.h>
|
|
//int set_system_date(time_t date)
|
|
//{
|
|
// if (settimeofday(&(struct timeval){.tv_sec = date, .tv_usec = 0}, NULL) < 0) {
|
|
// perror("settimeofday");
|
|
// return 1;
|
|
// }
|
|
// return 0;
|
|
//}
|
|
|
|
int main(const int argc, const char **argv)
|
|
{
|
|
if (argc < 2) {
|
|
fprintf(stderr, "usage: ./ntp [ntp server hostname]\n");
|
|
return 1;
|
|
}
|
|
int ntp_server_fd;
|
|
|
|
// create UDP connection
|
|
if ((ntp_server_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
|
perror("socket");
|
|
return 1;
|
|
}
|
|
|
|
const struct timeval time_val = {.tv_sec = 4, .tv_usec = 0};
|
|
if (setsockopt(ntp_server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &(int){1}, sizeof(int)) < 0
|
|
|| setsockopt(ntp_server_fd, SOL_SOCKET, SO_RCVTIMEO, &time_val, sizeof(struct timeval)) < 0
|
|
|| setsockopt(ntp_server_fd, SOL_SOCKET, SO_SNDTIMEO, &time_val, sizeof(struct timeval)) < 0
|
|
) {
|
|
perror("setsockopt");
|
|
return 1;
|
|
}
|
|
|
|
struct addrinfo *res;
|
|
struct addrinfo hints = {
|
|
.ai_family = AF_UNSPEC,
|
|
.ai_socktype = SOCK_DGRAM,
|
|
.ai_protocol = IPPROTO_UDP,
|
|
.ai_flags = 0,
|
|
};
|
|
int ret_val = 0;
|
|
if ((ret_val = getaddrinfo(argv[1], MACRO_INT2STR(NTP_PORT), &hints, &res)) != 0) {
|
|
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret_val));
|
|
return 1;
|
|
}
|
|
|
|
for (; res != NULL; res = res->ai_next) {
|
|
//char h[NI_MAXHOST] = {0};
|
|
//getnameinfo(res->ai_addr, res->ai_addrlen, h, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
|
//printf("getaddrinfo ip: %s\n", h);
|
|
if (connect(ntp_server_fd, res->ai_addr, res->ai_addrlen) < 0) {
|
|
perror("connect");
|
|
freeaddrinfo(res);
|
|
return 1;
|
|
} else { // if zero, we already connected
|
|
break;
|
|
}
|
|
}
|
|
freeaddrinfo(res);
|
|
|
|
struct ntp_packet ntp_pk = {
|
|
.leap_ver_mode = 0x23, // leap = 0, vn = 4, mode = 3 (client)
|
|
};
|
|
|
|
// send packet
|
|
if (send(ntp_server_fd, &ntp_pk, sizeof(struct ntp_packet), 0) < 0) {
|
|
perror("send");
|
|
return 1;
|
|
}
|
|
// receive answer packet from the server
|
|
if (recv(ntp_server_fd, &ntp_pk, sizeof(struct ntp_packet), 0) < 0) {
|
|
perror("recv");
|
|
return 1;
|
|
}
|
|
|
|
// close connection with the server
|
|
shutdown(ntp_server_fd, SHUT_RDWR);
|
|
close(ntp_server_fd);
|
|
|
|
ntp_pk.leap_ver_mode = ntohs(ntp_pk.leap_ver_mode);
|
|
ntp_pk.stratum = ntohs(ntp_pk.stratum);
|
|
//ntp_pk.ref_id = ntohl(ntp_pk.ref_id);
|
|
printf("NTP version: %u\n", ntp_pk.leap_ver_mode);
|
|
printf("stratum: %u\n", ntp_pk.stratum);
|
|
|
|
if ((ntp_pk.leap_ver_mode & 0x38) >= NTP_CURRENT_VER) {
|
|
fprintf(stderr, "invalid version number, got %u", ntp_pk.leap_ver_mode & 0x38);
|
|
return 1;
|
|
}
|
|
// check if it is a KoD packet
|
|
if (ntp_pk.stratum == 0) {
|
|
char kod_msg[5] = {0};
|
|
memcpy(&kod_msg, &ntp_pk.ref_id, 4);
|
|
printf("KoD msg: %s\n", kod_msg);
|
|
}
|
|
ntp_pk.tx_tstamp_sec = ntohl(ntp_pk.tx_tstamp_sec);
|
|
ntp_pk.tx_tstamp_frac = ntohl(ntp_pk.tx_tstamp_frac);
|
|
|
|
time_t ntp_time = (time_t)(ntp_pk.tx_tstamp_sec - NTP_UNIX_DELTA);
|
|
char fmt_time[64] = {0};
|
|
|
|
strftime(fmt_time, sizeof(fmt_time), "%Y-%m-%d %H:%M:%S%z", localtime(&ntp_time));
|
|
printf("Server NTP timestamp: %u (frac): %u\n", ntp_pk.tx_tstamp_sec, ntp_pk.tx_tstamp_frac);
|
|
printf("Server NTP time (ctime fmt'd): %s", ctime(&ntp_time));
|
|
printf("Server NTP time (ISO 8601): %s\n", fmt_time);
|
|
|
|
return 0;
|
|
}
|