// 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 #include #include #include #include #include #include #include #include #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 //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; }