From 349843cb326ee7e88ac595a06b42dc41a58c7247 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Fri, 30 Jan 2009 08:19:28 +0000 Subject: release 0.9 - inverted -b option - option -n turns on verbose - set time before forking so we lock til time is correct - fix usage - print version with -V --- Makefile | 7 +- README | 12 +++ client.c | 359 --------------------------------------------------------------- log.c | 2 +- sntp.c | 284 -------------------------------------------------- sntpc.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 306 insertions(+), 647 deletions(-) create mode 100644 README delete mode 100644 client.c delete mode 100644 sntp.c create mode 100644 sntpc.c diff --git a/Makefile b/Makefile index 260db1a..c41aa63 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,13 @@ -TARGET=sntp -SRC=sntp.c log.c util.c +TARGET=sntpc +SRC=sntpc.c log.c util.c DISTFILES=$(SRC) Makefile ntp.h util.h log.h -VERSION=0.8 +VERSION=0.9 P=$(TARGET)-$(VERSION) CFLAGS ?= -Wall -Werror -DDEBUG +CFLAGS += -DSNTPC_VERSION=\"$(VERSION)\" $(TARGET): $(SRC) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ diff --git a/README b/README new file mode 100644 index 0000000..a9e2832 --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +sntpc is a simple network time protocol client + +It was written to deal with time on virtual hosts on MS virtual server which +has a horrible virtual hardware clock. + +It will not make any efforts to slowly adjust the time, just set the time hard +from an NTP source, either once or in a given interval. + +To use it in a virtual environment, set the clocksource to "pit". This will +make the time go (mostly) backwards. Use the -b option to prevent sntpc to +make time jump backwards. + diff --git a/client.c b/client.c deleted file mode 100644 index 0eae0c5..0000000 --- a/client.c +++ /dev/null @@ -1,359 +0,0 @@ -/* $OpenBSD: client.c,v 1.79 2008/01/28 11:45:59 mpf Exp $ */ - -/* - * Copyright (c) 2003, 2004 Henning Brauer - * Copyright (c) 2004 Alexander Guy - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include "ntpd.h" - -int client_update(struct ntp_peer *); -void set_deadline(struct ntp_peer *, time_t); - -void -set_next(struct ntp_peer *p, time_t t) -{ - p->next = getmonotime() + t; - p->deadline = 0; -} - -void -set_deadline(struct ntp_peer *p, time_t t) -{ - p->deadline = getmonotime() + t; - p->next = 0; -} - -int -client_peer_init(struct ntp_peer *p) -{ - if ((p->query = calloc(1, sizeof(struct ntp_query))) == NULL) - fatal("client_peer_init calloc"); - p->query->fd = -1; - p->query->msg.status = MODE_CLIENT | (NTP_VERSION << 3); - p->state = STATE_NONE; - p->shift = 0; - p->trustlevel = TRUSTLEVEL_PATHETIC; - p->lasterror = 0; - p->senderrors = 0; - - return (client_addr_init(p)); -} - -int -client_addr_init(struct ntp_peer *p) -{ - struct sockaddr_in *sa_in; - struct sockaddr_in6 *sa_in6; - struct ntp_addr *h; - - for (h = p->addr; h != NULL; h = h->next) { - switch (h->ss.ss_family) { - case AF_INET: - sa_in = (struct sockaddr_in *)&h->ss; - if (ntohs(sa_in->sin_port) == 0) - sa_in->sin_port = htons(123); - p->state = STATE_DNS_DONE; - break; - case AF_INET6: - sa_in6 = (struct sockaddr_in6 *)&h->ss; - if (ntohs(sa_in6->sin6_port) == 0) - sa_in6->sin6_port = htons(123); - p->state = STATE_DNS_DONE; - break; - default: - fatalx("king bula sez: wrong AF in client_addr_init"); - /* not reached */ - } - } - - p->query->fd = -1; - set_next(p, 0); - - return (0); -} - -int -client_nextaddr(struct ntp_peer *p) -{ - if (p->query->fd != -1) { - close(p->query->fd); - p->query->fd = -1; - } - - if (p->state == STATE_DNS_INPROGRESS) - return (-1); - - if (p->addr_head.a == NULL) { - priv_host_dns(p->addr_head.name, p->id); - p->state = STATE_DNS_INPROGRESS; - return (-1); - } - - if ((p->addr = p->addr->next) == NULL) - p->addr = p->addr_head.a; - - p->shift = 0; - p->trustlevel = TRUSTLEVEL_PATHETIC; - - return (0); -} - -int -client_query(struct ntp_peer *p) -{ - int tos = IPTOS_LOWDELAY; - - if (p->addr == NULL && client_nextaddr(p) == -1) { - set_next(p, MAX(SETTIME_TIMEOUT, - scale_interval(INTERVAL_QUERY_AGGRESSIVE))); - return (0); - } - - if (p->state < STATE_DNS_DONE || p->addr == NULL) - return (-1); - - if (p->query->fd == -1) { - struct sockaddr *sa = (struct sockaddr *)&p->addr->ss; - - if ((p->query->fd = socket(p->addr->ss.ss_family, SOCK_DGRAM, - 0)) == -1) - fatal("client_query socket"); - if (connect(p->query->fd, sa, SA_LEN(sa)) == -1) { - if (errno == ECONNREFUSED || errno == ENETUNREACH || - errno == EHOSTUNREACH || errno == EADDRNOTAVAIL) { - client_nextaddr(p); - set_next(p, MAX(SETTIME_TIMEOUT, - scale_interval(INTERVAL_QUERY_AGGRESSIVE))); - return (-1); - } else - fatal("client_query connect"); - } - if (p->addr->ss.ss_family == AF_INET && setsockopt(p->query->fd, - IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) - log_warn("setsockopt IPTOS_LOWDELAY"); - } - - /* - * Send out a random 64-bit number as our transmit time. The NTP - * server will copy said number into the originate field on the - * response that it sends us. This is totally legal per the SNTP spec. - * - * The impact of this is two fold: we no longer send out the current - * system time for the world to see (which may aid an attacker), and - * it gives us a (not very secure) way of knowing that we're not - * getting spoofed by an attacker that can't capture our traffic - * but can spoof packets from the NTP server we're communicating with. - * - * Save the real transmit timestamp locally. - */ - - p->query->msg.xmttime.int_partl = arc4random(); - p->query->msg.xmttime.fractionl = arc4random(); - p->query->xmttime = gettime_corrected(); - - if (ntp_sendmsg(p->query->fd, NULL, &p->query->msg, - NTP_MSGSIZE_NOAUTH, 0) == -1) { - p->senderrors++; - set_next(p, INTERVAL_QUERY_PATHETIC); - p->trustlevel = TRUSTLEVEL_PATHETIC; - return (-1); - } - - p->senderrors = 0; - p->state = STATE_QUERY_SENT; - set_deadline(p, QUERYTIME_MAX); - - return (0); -} - -int -client_dispatch(struct ntp_peer *p, u_int8_t settime) -{ - char buf[NTP_MSGSIZE]; - ssize_t size; - struct ntp_msg msg; - double T1, T2, T3, T4; - time_t interval; - - if ((size = recvfrom(p->query->fd, &buf, sizeof(buf), 0, - NULL, NULL)) == -1) { - if (errno == EHOSTUNREACH || errno == EHOSTDOWN || - errno == ENETUNREACH || errno == ENETDOWN || - errno == ECONNREFUSED || errno == EADDRNOTAVAIL) { - client_log_error(p, "recvfrom", errno); - set_next(p, error_interval()); - return (0); - } else - fatal("recvfrom"); - } - - T4 = gettime_corrected(); - - ntp_getmsg((struct sockaddr *)&p->addr->ss, buf, size, &msg); - - if (msg.orgtime.int_partl != p->query->msg.xmttime.int_partl || - msg.orgtime.fractionl != p->query->msg.xmttime.fractionl) - return (0); - - if ((msg.status & LI_ALARM) == LI_ALARM || msg.stratum == 0 || - msg.stratum > NTP_MAXSTRATUM) { - interval = error_interval(); - set_next(p, interval); - log_info("reply from %s: not synced, next query %ds", - log_sockaddr((struct sockaddr *)&p->addr->ss), interval); - return (0); - } - - /* - * From RFC 2030 (with a correction to the delay math): - * - * Timestamp Name ID When Generated - * ------------------------------------------------------------ - * Originate Timestamp T1 time request sent by client - * Receive Timestamp T2 time request received by server - * Transmit Timestamp T3 time reply sent by server - * Destination Timestamp T4 time reply received by client - * - * The roundtrip delay d and local clock offset t are defined as - * - * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2. - */ - - T1 = p->query->xmttime; - T2 = lfp_to_d(msg.rectime); - T3 = lfp_to_d(msg.xmttime); - - p->reply[p->shift].offset = ((T2 - T1) + (T3 - T4)) / 2; - p->reply[p->shift].delay = (T4 - T1) - (T3 - T2); - if (p->reply[p->shift].delay < 0) { - interval = error_interval(); - set_next(p, interval); - log_info("reply from %s: negative delay %fs, " - "next query %ds", - log_sockaddr((struct sockaddr *)&p->addr->ss), - p->reply[p->shift].delay, interval); - return (0); - } - p->reply[p->shift].error = (T2 - T1) - (T3 - T4); - p->reply[p->shift].rcvd = getmonotime(); - p->reply[p->shift].good = 1; - - p->reply[p->shift].status.leap = (msg.status & LIMASK); - p->reply[p->shift].status.precision = msg.precision; - p->reply[p->shift].status.rootdelay = sfp_to_d(msg.rootdelay); - p->reply[p->shift].status.rootdispersion = sfp_to_d(msg.dispersion); - p->reply[p->shift].status.refid = ntohl(msg.refid); - p->reply[p->shift].status.refid4 = msg.xmttime.fractionl; - p->reply[p->shift].status.reftime = lfp_to_d(msg.reftime); - p->reply[p->shift].status.poll = msg.ppoll; - p->reply[p->shift].status.stratum = msg.stratum; - - if (p->addr->ss.ss_family == AF_INET) - p->reply[p->shift].status.send_refid = - ((struct sockaddr_in *)&p->addr->ss)->sin_addr.s_addr; - else - p->reply[p->shift].status.send_refid = msg.xmttime.fractionl; - - if (p->trustlevel < TRUSTLEVEL_PATHETIC) - interval = scale_interval(INTERVAL_QUERY_PATHETIC); - else if (p->trustlevel < TRUSTLEVEL_AGGRESSIVE) - interval = scale_interval(INTERVAL_QUERY_AGGRESSIVE); - else - interval = scale_interval(INTERVAL_QUERY_NORMAL); - - set_next(p, interval); - p->state = STATE_REPLY_RECEIVED; - - /* every received reply which we do not discard increases trust */ - if (p->trustlevel < TRUSTLEVEL_MAX) { - if (p->trustlevel < TRUSTLEVEL_BADPEER && - p->trustlevel + 1 >= TRUSTLEVEL_BADPEER) - log_info("peer %s now valid", - log_sockaddr((struct sockaddr *)&p->addr->ss)); - p->trustlevel++; - } - - log_debug("reply from %s: offset %f delay %f, " - "next query %ds", log_sockaddr((struct sockaddr *)&p->addr->ss), - p->reply[p->shift].offset, p->reply[p->shift].delay, interval); - - client_update(p); - if (settime) - priv_settime(p->reply[p->shift].offset); - - if (++p->shift >= OFFSET_ARRAY_SIZE) - p->shift = 0; - - return (0); -} - -int -client_update(struct ntp_peer *p) -{ - int i, best = 0, good = 0; - - /* - * clock filter - * find the offset which arrived with the lowest delay - * use that as the peer update - * invalidate it and all older ones - */ - - for (i = 0; good == 0 && i < OFFSET_ARRAY_SIZE; i++) - if (p->reply[i].good) { - good++; - best = i; - } - - for (; i < OFFSET_ARRAY_SIZE; i++) - if (p->reply[i].good) { - good++; - if (p->reply[i].delay < p->reply[best].delay) - best = i; - } - - if (good < 8) - return (-1); - - memcpy(&p->update, &p->reply[best], sizeof(p->update)); - if (priv_adjtime() == 0) { - for (i = 0; i < OFFSET_ARRAY_SIZE; i++) - if (p->reply[i].rcvd <= p->reply[best].rcvd) - p->reply[i].good = 0; - } - return (0); -} - -void -client_log_error(struct ntp_peer *peer, const char *operation, int error) -{ - const char *address; - - address = log_sockaddr((struct sockaddr *)&peer->addr->ss); - if (peer->lasterror == error) { - log_debug("%s %s: %s", operation, address, strerror(error)); - return; - } - peer->lasterror = error; - log_warn("%s %s", operation, address); -} diff --git a/log.c b/log.c index 29bd66f..031fa63 100644 --- a/log.c +++ b/log.c @@ -13,7 +13,7 @@ static int verbose; void log_init(int v) { verbose = v; - openlog("sntpd", LOG_PERROR | LOG_PID, LOG_DAEMON); + openlog("sntpc", LOG_PERROR | LOG_PID, LOG_DAEMON); } diff --git a/sntp.c b/sntp.c deleted file mode 100644 index 2117bef..0000000 --- a/sntp.c +++ /dev/null @@ -1,284 +0,0 @@ -/* Copyright (c) 2008 Natanael Copa - * - * Licencend under GPL-2 - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "ntp.h" -#include "util.h" - -static int allow_backward = 0; -static int background = 0; -static int dryrun = 0; -static int interval = 120; -static int verbosity = 0; -static const char* ntphost; - -static const char *usage_str = "usage: sntpd [-bhnV] [-i interval] NTPHOST\n" -"options:\n" -" -b Allow time go backwards\n" -" -d Fork to background (daemonize)\n" -" -h Show this help\n" -" -n Do not set time\n" -" -i Set poll 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++; - break; - case 'd': - background++; - break; - case 'h': - printf(usage_str); - return(0); - case 'i': - interval = atoi(optarg); - break; - case 'n': - dryrun++; - break; - case 'V': - printf("Version\n"); - return(0); - case 'v': - verbosity++; - break; - } - } - if (optind != (argc - 1)) { - printf(usage_str); - return 1; - } - ntphost = argv[optind]; - - if (background) - daemonize(); - - log_init(verbosity); - /* main loop */ - while (1) { - sync_time(ntphost); - sleep(interval); - } - return 0; -} diff --git a/sntpc.c b/sntpc.c new file mode 100644 index 0000000..61724ad --- /dev/null +++ b/sntpc.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2008 Natanael Copa + * + * Licencend under GPL-2 + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3