summaryrefslogtreecommitdiffstats
path: root/sntpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sntpc.c')
-rw-r--r--sntpc.c289
1 files changed, 289 insertions, 0 deletions
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 <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;
+}