Show ntp.c syntax highlighted
/*
* $Id: ntp.c 13777 2007-05-29 00:23:11Z cbiere $
*
* Copyright (c) 2004, Raphael Manfredi
*
*----------------------------------------------------------------------
* This file is part of gtk-gnutella.
*
* gtk-gnutella is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* gtk-gnutella is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gtk-gnutella; if not, write to the Free Software
* Foundation, Inc.:
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*----------------------------------------------------------------------
*/
/**
* @ingroup core
* @file
*
* Detection of a local NTP server.
*
* @author Raphael Manfredi
* @date 2004
*/
#include "common.h"
RCSID("$Id: ntp.c 13777 2007-05-29 00:23:11Z cbiere $")
#include "ntp.h"
#include "settings.h"
#include "sockets.h"
#include "if/core/hosts.h"
#include "if/gnet_property.h"
#include "if/gnet_property_priv.h"
#include "lib/cq.h"
#include "lib/endian.h"
#include "lib/misc.h"
#include "lib/tm.h"
#include "lib/override.h" /* Must be the last header included */
#define OFFSET_1900 2208988800U /**< secs between 1900 and the Epoch */
#define NTP_FP_SCALE 4294967296.0 /**< NTP fixed-point scaling base */
#define NTP_WAIT_MS (5*1000) /**< Wait at most 5 secs for reply */
#define NTP_VERSION 3 /**< Say we're version 3 */
#define NTP_CLIENT 3 /**< Mode for unicast request */
#define NTP_SERVER 4 /**< Mode for server reply */
#define NTP_AUTHSIZE 20 /**< Size of NTP auth, when present */
#define NTP_MINSIZE sizeof(struct ntp_msg)
#define NTP_MAXSIZE (NTP_MINSIZE + NTP_AUTHSIZE)
static cevent_t *wait_ev; /**< Callout queue waiting event */
/**
* An NTP message, as described in RFC2030 (trailing auth-data ignored).
*/
struct ntp_msg {
guint8 flags;
guint8 stratum;
guint8 poll;
guint8 precision;
guchar root_delay[4];
guchar root_dispersion[4];
guchar reference_id[4];
guchar reference_timestamp[8];
guchar originate_timestamp[8];
guchar receive_timestamp[8];
guchar transmit_timestamp[8];
};
/**
* Fill 8-byte buffer with NTP's representation of a tm_t time.
*/
static void
ntp_tm_serialize(guchar dest[8], tm_t *t)
{
poke_be32(&dest[0], t->tv_sec + OFFSET_1900);
poke_be32(&dest[4], t->tv_usec * 1.0e-6 * NTP_FP_SCALE);
}
/**
* Construct a tm_t time from an NTP timestamp.
*/
static void
ntp_tm_deserialize(const guchar src[8], tm_t *dest)
{
dest->tv_sec = peek_be32(&src[0]) - OFFSET_1900;
dest->tv_usec = (guint32) (peek_be32(&src[4]) * 1.0e6 / NTP_FP_SCALE);
}
/**
* Callout queue timeout when no reply is received within a reasonable
* timeframe.
*/
static void
ntp_no_reply(cqueue_t *unused_cq, gpointer unused_udata)
{
(void) unused_cq;
(void) unused_udata;
if (GNET_PROPERTY(dbg))
printf("NTP no reply from localhost\n");
/*
* Don't set PROP_HOST_RUNS_NTP to FALSE. If they force it to TRUE,
* because they run "ntpdate" once in a while, we'll ignore the
* computed clock skew but we won't advertise to others that we're
* running NTP if we haven't detected it.
* --RAM, 2004-10-27.
*/
gnet_prop_set_boolean_val(PROP_NTP_DETECTED, FALSE);
wait_ev = NULL;
}
static gboolean
ntp_send_probe(const host_addr_t addr)
{
static const struct ntp_msg zero_m;
struct gnutella_socket *s;
struct ntp_msg m;
gnet_host_t to;
tm_t now;
ssize_t r;
m = zero_m;
m.flags = (NTP_VERSION << 3) | NTP_CLIENT;
tm_now_exact(&now);
ntp_tm_serialize(m.transmit_timestamp, &now);
gnet_host_set(&to, addr, NTP_PORT);
s = NULL;
switch (host_addr_net(addr)) {
case NET_TYPE_IPV4:
s = s_udp_listen;
break;
case NET_TYPE_IPV6:
s = s_udp_listen6;
break;
case NET_TYPE_LOCAL:
case NET_TYPE_NONE:
break;
}
if (!s) {
errno = EINVAL;
return FALSE;
}
r = s->wio.sendto(&s->wio, &to, &m, sizeof m);
/* Reset errno if there was no "real" error to prevent getting a
* bogus and possibly misleading error message later. */
if ((ssize_t) -1 != r)
errno = 0;
return r == sizeof m;
}
static gboolean
ntp_send_probes(void)
{
static const struct {
const gchar *addr;
} hosts[] = {
#if 0
/* Skip this for now. We check replies only against 127.0.0.1 and ::1
* anyway and there is also the minor DNS issue below. */
{ "localhost" },
#endif
{ "::1" },
{ "127.0.0.1" },
};
gboolean sent = FALSE;
guint i;
/* TODO: The name_to_host_addr() could take a while which would
* delay startup. Thus, use ADNS for this.
*/
for (i = 0; i < G_N_ELEMENTS(hosts); i++) {
host_addr_t addr;
addr = name_to_single_host_addr(hosts[i].addr, settings_dns_net());
if (!is_host_addr(addr))
continue;
if (ntp_send_probe(addr)) {
/* Send probes to all addresses because a successful sendto()
* does not guarantee anything. */
sent = TRUE;
} else if (GNET_PROPERTY(dbg)) {
g_message("ntp_probe(): sendto() failed for \"%s\" (\"%s\"): %s",
hosts[i].addr,
host_addr_to_string(addr),
g_strerror(errno));
}
}
return sent;
}
/**
* Send an NTP probe to the local host using the loopback addresses 127.0.0.1
* and ::1, waiting for a reply within the next NTP_WAIT_MS ms.
*/
void
ntp_probe(void)
{
if (!udp_active() || !ntp_send_probes())
return;
/*
* Arm timer to see whether we get a reply in the next NTP_WAIT_MS.
*/
if (GNET_PROPERTY(dbg))
printf("NTP sent probe to localhost\n");
cq_cancel(callout_queue, &wait_ev);
wait_ev = cq_insert(callout_queue, NTP_WAIT_MS, ntp_no_reply, NULL);
}
/**
* Got a reply from an NTP daemon.
*/
void
ntp_got_reply(struct gnutella_socket *s)
{
struct ntp_msg *m;
guint8 version;
guint8 mode;
tm_t received;
tm_t sent;
tm_t replied;
tm_t got;
tm_t offset;
gdouble clock_offset;
tm_now_exact(&got);
if (s->pos != NTP_MINSIZE && s->pos != NTP_MAXSIZE) {
g_warning("got weird reply from NTP server (%d bytes)", (gint) s->pos);
return;
}
m = (struct ntp_msg *) s->buf;
mode = m->flags & 0x7;
version = (m->flags >> 3) & 0x7;
if (mode != NTP_SERVER) {
g_warning("got reply from NTP server with weird mode (%d)", mode);
return;
}
if (GNET_PROPERTY(dbg))
printf("NTP got %s reply from NTP-%u server, stratum %u\n",
s->pos == NTP_MINSIZE ? "regular" : "auth", version, m->stratum);
/*
* Since we know NTP runs locally, disarm the timeout.
*/
cq_cancel(callout_queue, &wait_ev);
gnet_prop_set_boolean_val(PROP_HOST_RUNS_NTP, TRUE);
gnet_prop_set_boolean_val(PROP_NTP_DETECTED, TRUE);
/*
* Compute the initial clock offset.
* This is given by: ((received - sent) + (replied - got)) / 2
*/
ntp_tm_deserialize(m->originate_timestamp, &sent);
ntp_tm_deserialize(m->receive_timestamp, &received);
ntp_tm_deserialize(m->transmit_timestamp, &replied);
offset = received; /* Struct copy */
tm_sub(&offset, &sent);
tm_add(&offset, &replied);
tm_sub(&offset, &got);
clock_offset = tm2f(&offset) / 2; /* Should be close to 0 */
if (GNET_PROPERTY(dbg) > 1)
printf("NTP local clock offset is %.6f secs\n",
(double) clock_offset);
gnet_prop_set_guint32_val(PROP_CLOCK_SKEW, (guint32) clock_offset);
g_message("detected NTP-%u, stratum %u, offset %.6f secs",
version, m->stratum, (double) clock_offset);
}
/**
* Initialize the NTP monitoring.
*/
void
ntp_init(void)
{
ntp_probe();
}
/**
* Shutdown the NTP monitoring.
*/
void
ntp_close(void)
{
cq_cancel(callout_queue, &wait_ev);
}
/* vi: set ts=4 sw=4 cindent: */
See more files for this project here