diff options
-rw-r--r-- | Makefile | 25 | ||||
-rw-r--r-- | client.c | 359 | ||||
-rw-r--r-- | log.c | 72 | ||||
-rw-r--r-- | log.h | 9 | ||||
-rw-r--r-- | ntp.h | 150 | ||||
-rw-r--r-- | sntp.c | 284 | ||||
-rw-r--r-- | util.c | 100 | ||||
-rw-r--r-- | util.h | 9 |
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); +} @@ -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); +} + @@ -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); @@ -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_ */ @@ -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; +} @@ -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 @@ -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); + + |