diff options
Diffstat (limited to 'sntpc.c')
-rw-r--r-- | sntpc.c | 289 |
1 files changed, 289 insertions, 0 deletions
@@ -0,0 +1,289 @@ +/* Copyright (c) 2008 Natanael Copa + * + * Licencend under GPL-2 + */ +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <fcntl.h> +#include <netdb.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "log.h" +#include "ntp.h" +#include "util.h" + +static int allow_backward = 1; +static int background = 0; +static int dryrun = 0; +static int interval = 0; +static int verbosity = 0; +static const char* ntphost; + +static const char *usage_str = "usage: sntpc [-bdhnvV] [-i INTERVAL] NTPHOST\n" +"options:\n" +" -b Do not allow time jump backwards\n" +" -d Fork to background (daemonize)\n" +" -h Show this help\n" +" -n Do not set time, just show the offset and delay\n" +" -i Poll every INTERVAL seconds\n" +" -v Verbose logging\n" +" -V Show version and exit\n" +"\n"; + + + +int init_conn(const char *host, const char *service) { + struct addrinfo hints; + struct addrinfo *res, *res0; + int fd, error; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + error = getaddrinfo(host, service, &hints, &res0); + if (error != 0) { + log_error("getaddrinfo: %s", gai_strerror(error)); + return -1; + } + + for (res = res0; res != NULL; res = res->ai_next) { + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd == -1) + continue; + + if (connect(fd, res->ai_addr, res->ai_addrlen) != -1) + break; + log_perror("connect"); + close(fd); + } + freeaddrinfo(res0); + if (res == NULL) { + return -1; + } + return fd; +} + +long int get_random(void) +{ + long int r; + int fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + open("/dev/random", O_RDONLY); + + if (fd == -1) + return random(); /* not very secure... */ + + read(fd, &r, sizeof(r)); + close(fd); + return r; +} + +void ntp_msg_init_query(struct ntp_msg *msg) +{ + memset(msg, 0, sizeof(struct ntp_msg)); + msg->status = MODE_CLIENT | (NTP_VERSION << 3); + /* we dont really want to tell what our time is */ + msg->xmttime.int_partl = get_random(); + msg->xmttime.fractionl = get_random(); +} + + +int ntp_msg_send(int fd, struct ntp_msg *msg) +{ + int ret = send(fd, msg, sizeof(struct ntp_msg), 0); + if (ret == -1) + log_perror("send"); + return ret; +} + +int ntp_msg_wait(int fd, int timeout) +{ + struct pollfd fds; + int ret; + fds.fd = fd; + fds.events = POLLIN; + ret = poll(&fds, 1, timeout); + if (ret < 0) + log_perror("poll"); + return ret; +} + +int ntp_msg_recv(int fd, struct ntp_msg *msg) +{ + char buf[NTP_MSGSIZE]; + ssize_t size = recv(fd, &buf, sizeof(buf), 0); + if (size < 0) { + log_perror("recv"); + return -1; + } + + if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { + log_warning("malformated packet recieved"); + return -1; + } + + memcpy(msg, buf, sizeof(*msg)); + return 0; +} + +int ntp_msg_query(int fd, struct ntp_msg *response, + double *T1, double *T4) +{ + int retry = 3; + static struct ntp_msg query; + + ntp_msg_init_query(&query); + while (retry--) { + *T1 = gettime(); + if (ntp_msg_send(fd, &query) < 0) + return -1; + + if (ntp_msg_wait(fd, 3000) <= 0) + continue; + + *T4 = gettime(); + if (ntp_msg_recv(fd, response) == 0) + break; + } + if (retry == 0) + return -1; + + if (query.xmttime.int_partl != response->orgtime.int_partl || + query.xmttime.fractionl != response->orgtime.fractionl) + return -1; + + return 0; +} + +#if DEBUG +void ntp_msg_dump(struct ntp_msg *msg) +{ + printf("status: %x\n",msg->status); + printf("stratum: %u\n", msg->stratum); + printf("poll: %u\n", msg->ppoll); +} +#endif + +int settime(double offset) +{ + struct timeval o, n; + + if (gettimeofday(&n, NULL) < 0) { + log_perror("gettimeofday"); + return -1; + } + + d_to_tv(offset, &o); + n.tv_usec += o.tv_usec + 1000000; + n.tv_sec += o.tv_sec - 1 + (n.tv_usec / 1000000); + n.tv_usec %= 1000000; + + if (settimeofday(&n, NULL) < 0) { + log_perror("settimeofday"); + return -1; + } + return 0; +} + +int sync_time(const char *host) +{ + static struct ntp_msg response; + int ret; + int fd; + double T1, T2, T3, T4, delay, offset; + + fd = init_conn(host, "ntp"); + if (fd < 0) + return -1; + + ret = ntp_msg_query(fd, &response, &T1, &T4); + close(fd); + if (ret < 0) + return -1; +#if DEBUG +// ntp_msg_dump(&response); +#endif + T2 = lfp_to_d(response.rectime); + T3 = lfp_to_d(response.xmttime); + + offset = ((T2 - T1) + (T3 - T4)) / 2; + delay = (T4 - T1) - (T3 - T2); + log_debug("offset=%f, delay=%f", offset, delay); + + if (dryrun || (offset < 0 && !allow_backward)) + return 0; + + return settime(offset); +} + +void daemonize(void) { + int pid = fork(); + if (pid < 0) + fatal("fork"); + + if (pid > 0) + exit(0); + + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); +} + +int main(int argc, char * const argv[]) +{ + int c; + while ((c = getopt(argc, argv, "bdhi:nVv")) > 0) { + switch (c) { + case 'b': + allow_backward = 0; + break; + case 'd': + background++; + break; + case 'h': + printf(usage_str); + return(0); + case 'i': + interval = atoi(optarg); + break; + case 'n': + dryrun++; + verbosity++; + break; + case 'V': + puts("sntpc " SNTPC_VERSION); + return(0); + case 'v': + verbosity++; + break; + } + } + if (optind != (argc - 1)) { + printf(usage_str); + return 1; + } + ntphost = argv[optind]; + + log_init(verbosity); + + /* set time once before daemonize. This will make sure that we lock + til we have set the time correctly. */ + c = sync_time(ntphost); + + if (background) + daemonize(); + + while (interval) { + sleep(interval); + sync_time(ntphost); + } + return c; +} |