summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile25
-rw-r--r--client.c359
-rw-r--r--log.c72
-rw-r--r--log.h9
-rw-r--r--ntp.h150
-rw-r--r--sntp.c284
-rw-r--r--util.c100
-rw-r--r--util.h9
8 files changed, 1008 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..260db1a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+TARGET=sntp
+SRC=sntp.c log.c util.c
+
+DISTFILES=$(SRC) Makefile ntp.h util.h log.h
+
+VERSION=0.8
+P=$(TARGET)-$(VERSION)
+
+CFLAGS ?= -Wall -Werror -DDEBUG
+
+$(TARGET): $(SRC)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
+
+clean:
+ rm -f $(TARGET)
+
+tar: $(P).tar.gz
+
+$(P).tar.gz: $(DISTFILES)
+ rm -rf $(P)
+ mkdir -p $(P)
+ cp $(DISTFILES) $(P)
+ tar -czf $@ $(P)
+ rm -rf $(P)
+
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..0eae0c5
--- /dev/null
+++ b/client.c
@@ -0,0 +1,359 @@
+/* $OpenBSD: client.c,v 1.79 2008/01/28 11:45:59 mpf Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
+ *
+ * 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 <sys/param.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#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
new file mode 100644
index 0000000..29bd66f
--- /dev/null
+++ b/log.c
@@ -0,0 +1,72 @@
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "log.h"
+
+static int verbose;
+
+void log_init(int v)
+{
+ verbose = v;
+ openlog("sntpd", LOG_PERROR | LOG_PID, LOG_DAEMON);
+}
+
+
+void log_error(const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ vsyslog(LOG_ERR, format, va);
+ va_end(va);
+}
+
+
+void log_perror(const char *message)
+{
+ log_error("%s: %s", message, strerror(errno));
+}
+
+
+void log_warning(const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ vsyslog(LOG_WARNING, format, va);
+ va_end(va);
+}
+
+void log_critical(const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ vsyslog(LOG_CRIT, format, va);
+ va_end(va);
+}
+
+void log_debug(const char *format, ...)
+{
+ if (verbose) {
+ va_list va;
+ va_start(va, format);
+ vsyslog(LOG_DEBUG, format, va);
+ va_end(va);
+ }
+}
+
+void fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ log_critical("fatal: %s", strerror(errno));
+ else
+ if (errno)
+ log_critical("fatal: %s: %s", emsg, strerror(errno));
+ else
+ log_critical("fatal: %s", emsg);
+ exit(1);
+}
+
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..5efc65c
--- /dev/null
+++ b/log.h
@@ -0,0 +1,9 @@
+#include <stdarg.h>
+
+void log_init(int daemonized);
+void log_error(const char *format, ...);
+void log_perror(const char *message);
+void log_warning(const char *format, ...);
+void log_critical(const char *format, ...);
+void log_debug(const char *format, ...);
+void fatal(const char *emsg);
diff --git a/ntp.h b/ntp.h
new file mode 100644
index 0000000..bfca553
--- /dev/null
+++ b/ntp.h
@@ -0,0 +1,150 @@
+/* $OpenBSD: ntp.h,v 1.12 2007/05/26 21:20:35 henning Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
+ *
+ * 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 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.
+ */
+
+#ifndef _NTP_H_
+#define _NTP_H_
+
+#include <sys/types.h>
+
+/* Style borrowed from NTP ref/tcpdump and updated for SNTPv4 (RFC2030). */
+
+/*
+ * RFC Section 3
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+struct l_fixedpt {
+ u_int32_t int_partl;
+ u_int32_t fractionl;
+};
+
+struct s_fixedpt {
+ u_int16_t int_parts;
+ u_int16_t fractions;
+};
+
+/* RFC Section 4
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |LI | VN | Mode| Stratum | Poll | Precision |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Synchronizing Distance |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Synchronizing Dispersion |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reference Clock Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Reference Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Originate Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Receive Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Transmit Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key Identifier (optional) (32) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | |
+ * | Message Digest (optional) (128) |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+#define NTP_DIGESTSIZE 16
+#define NTP_MSGSIZE_NOAUTH 48
+#define NTP_MSGSIZE (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE)
+
+struct ntp_msg {
+ u_int8_t status; /* status of local clock and leap info */
+ u_int8_t stratum; /* Stratum level */
+ u_int8_t ppoll; /* poll value */
+ int8_t precision;
+ struct s_fixedpt rootdelay;
+ struct s_fixedpt dispersion;
+ u_int32_t refid;
+ struct l_fixedpt reftime;
+ struct l_fixedpt orgtime;
+ struct l_fixedpt rectime;
+ struct l_fixedpt xmttime;
+} __packed;
+
+struct ntp_query {
+ int fd;
+ struct ntp_msg msg;
+ double xmttime;
+};
+
+/*
+ * Leap Second Codes (high order two bits)
+ */
+#define LI_NOWARNING (0 << 6) /* no warning */
+#define LI_PLUSSEC (1 << 6) /* add a second (61 seconds) */
+#define LI_MINUSSEC (2 << 6) /* minus a second (59 seconds) */
+#define LI_ALARM (3 << 6) /* alarm condition */
+
+/*
+ * Status Masks
+ */
+#define MODEMASK (7 << 0)
+#define VERSIONMASK (7 << 3)
+#define LIMASK (3 << 6)
+
+/*
+ * Mode values
+ */
+#define MODE_RES0 0 /* reserved */
+#define MODE_SYM_ACT 1 /* symmetric active */
+#define MODE_SYM_PAS 2 /* symmetric passive */
+#define MODE_CLIENT 3 /* client */
+#define MODE_SERVER 4 /* server */
+#define MODE_BROADCAST 5 /* broadcast */
+#define MODE_RES1 6 /* reserved for NTP control message */
+#define MODE_RES2 7 /* reserved for private use */
+
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+
+#define NTP_VERSION 4
+#define NTP_MAXSTRATUM 15
+
+#endif /* _NTP_H_ */
diff --git a/sntp.c b/sntp.c
new file mode 100644
index 0000000..2117bef
--- /dev/null
+++ b/sntp.c
@@ -0,0 +1,284 @@
+/* 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 = 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/util.c b/util.c
new file mode 100644
index 0000000..1f30abc
--- /dev/null
+++ b/util.c
@@ -0,0 +1,100 @@
+/* $OpenBSD: util.c,v 1.13 2007/03/27 18:22:02 otto Exp $ */
+
+/*
+ * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
+ *
+ * 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 <arpa/inet.h>
+#include <sys/time.h>
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "ntp.h"
+#include "util.h"
+
+
+double
+gettime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) == -1)
+ fatal("gettimeofday");
+
+ return (tv.tv_sec + JAN_1970 + 1.0e-6 * tv.tv_usec);
+}
+
+
+void
+d_to_tv(double d, struct timeval *tv)
+{
+ tv->tv_sec = (long)d;
+ tv->tv_usec = (d - tv->tv_sec) * 1000000;
+ while (tv->tv_usec < 0) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec -= 1;
+ }
+}
+
+double
+lfp_to_d(struct l_fixedpt lfp)
+{
+ double ret;
+
+ lfp.int_partl = ntohl(lfp.int_partl);
+ lfp.fractionl = ntohl(lfp.fractionl);
+
+ ret = (double)(lfp.int_partl) + ((double)lfp.fractionl / UINT_MAX);
+
+ return (ret);
+}
+#if 0
+struct l_fixedpt
+d_to_lfp(double d)
+{
+ struct l_fixedpt lfp;
+
+ lfp.int_partl = htonl((u_int32_t)d);
+ lfp.fractionl = htonl((u_int32_t)((d - (u_int32_t)d) * UINT_MAX));
+
+ return (lfp);
+}
+
+double
+sfp_to_d(struct s_fixedpt sfp)
+{
+ double ret;
+
+ sfp.int_parts = ntohs(sfp.int_parts);
+ sfp.fractions = ntohs(sfp.fractions);
+
+ ret = (double)(sfp.int_parts) + ((double)sfp.fractions / USHRT_MAX);
+
+ return (ret);
+}
+
+struct s_fixedpt
+d_to_sfp(double d)
+{
+ struct s_fixedpt sfp;
+
+ sfp.int_parts = htons((u_int16_t)d);
+ sfp.fractions = htons((u_int16_t)((d - (u_int16_t)d) * USHRT_MAX));
+
+ return (sfp);
+}
+#endif
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..a4101b7
--- /dev/null
+++ b/util.h
@@ -0,0 +1,9 @@
+/* util.c */
+double gettime(void);
+void d_to_tv(double, struct timeval *);
+double lfp_to_d(struct l_fixedpt);
+struct l_fixedpt d_to_lfp(double);
+double sfp_to_d(struct s_fixedpt);
+struct s_fixedpt d_to_sfp(double);
+
+