Rob Seaman <[email protected]> wrote:
 |I think it’s clear that DNS won’t support all leap second \
 |use cases, but that it may provide a high reliability / low \
 |latency method for some specific purposes.  Here is PHK’s specific example:
 |
 | $ dig +short leap.net-tid.dk a | ./leapdecode.py
 | 248.40.141.250 -> OK  2015  7  +35 +1
 |
 |(dig might be useful for some script, but most usage will \
 |be more direct methods, of course)  Leapdecode.py is PHK’s \
 |crc8() and dec() functions with:

 |The next.leapsec.com <http://next.leapsec.com/> address could \
 |be coupled with prev.leapsec.com <http://prev.leapsec.com/> \
 |and other options.  Etc and so forth.

Cool!
Below a simple C version for the interested.  It doesn't iterate
over the results but only uses the first; it also doesn't provide
an encode mode.  Compile with either of (dependend on wether you
want gethostbyname(3) or getaddrinfo(3), written with the former):

  $0[]$ cc -o leapsec phk-utcdrift.c
  $0[]$ cc -DWANT_GETHOSTBYNAME -o leapsec phk-utcdrift.c

  ?0[]$ ./leapsec 248.40.141.250
  248.40.141.250 -> OK 2015-07 +35 +1
  ?0[]$ ./leapsec next.leapsec.com
  248.40.141.250 -> OK 2015-07 +35 +1
  ?0[]$ ./leapsec leap.net-tid.dk
  248.40.141.250 -> OK 2015-07 +35 +1
  ?0[]$ ./leapsec prev.leapsec.com
  247.152.137.102 -> OK 2012-07 +34 +1
  ?0[]$ ./leapsec google.com
  ! CRC checksum failure
  216.58.211.14 -> BAD 0000-00 +0 +0
  ?65[]$

Errors go to standard error, exit status uses BSD sysexits.h
constants.

--steffen
/*@ Fetch a PHK DNS A record and calculate current leapsecond status.
 *@ Compile:	$ cc -o leapsec phk-utcdrift.c
 *@ Fallback:	$ cc -DWANT_GETHOSTBYNAME -o leapsec phk-utcdrift.c
 *@ Synopsis:	$ leapsec SERVER
 *
 * Public Domain.
 */

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <netdb.h>

#ifndef NI_MAXHOST
# define NI_MAXHOST	1025
#endif

#define _EX_OK		EXIT_SUCCESS
#define _EX_USAGE	64
#define _EX_DATAERR	65
#define _EX_NOHOST	68

#ifndef NELEM
# define NELEM(X)	(sizeof(X) / sizeof((X)[0]))
#endif

typedef unsigned long	ul_i;
typedef unsigned int	ui_i;
typedef signed int	si_i;
typedef unsigned short	us_i;
typedef unsigned char	uc_i;
typedef signed char	sc_i;
typedef enum {FAL0, TRU1} bool_t;

struct dns_leapinfo {
	ul_i		dl_addr_parts[4];
	char		_dl_pad1[1];
	sc_i		dl_adjust;
	sc_i		dl_drift;
	uc_i		dl_month;
	us_i		dl_year;
	char		_dl_pad2[2];
	char		dl_addr[NI_MAXHOST];
};

static ul_i	_fetch_addr(char const *host, struct dns_leapinfo *dlp);
static bool_t	_dl_addr_to_aparts(struct dns_leapinfo *dlp);
static bool_t	_dl_crc8_phk(struct dns_leapinfo const *dlp);
static bool_t	_dl_explode(struct dns_leapinfo *dlp);

int
main(int argc, char **argv)
{
	struct dns_leapinfo dl;
	ul_i addr;
	int rv;

	rv = _EX_USAGE;
	if (argc != 2) {
		fprintf(stderr, "Synopsis: %s INET-ADDR\n", argv[0]);
		goto jleave;
	}

	rv = _EX_NOHOST;
	if ((addr = _fetch_addr(argv[1], &dl)) == -1) {
		fprintf(stderr, "! DNS failed to resolve `%s'\n", argv[1]);
		goto jleave;
	}

	rv = _EX_DATAERR;
	if (!_dl_addr_to_aparts(&dl)) {
		fprintf(stderr, "! Bogus IP address content: `%s'\n", dl.dl_addr);
		goto jprint;
	}

	if (!_dl_crc8_phk(&dl)) {
		fprintf(stderr, "! CRC checksum failure\n");
		goto jprint;
	}

	if (!_dl_explode(&dl)) {
		fprintf(stderr,
			"! Bogus leapsecond data / non-class E address: `%s'\n",
			dl.dl_addr);
		goto jprint;
	}

	rv = _EX_OK;
jprint:
	printf("%s -> %s %04u-%02u %c%d %c%d\n",
		dl.dl_addr,
		(rv == _EX_OK ? "OK" : "BAD"),
		(ui_i)dl.dl_year, (ui_i)dl.dl_month,
		(dl.dl_drift < 0 ? '-' : '+'), (si_i)dl.dl_drift,
		(dl.dl_adjust < 0 ? '-' : '+'), (si_i)dl.dl_adjust);
jleave:
	return rv;
}

#ifndef WANT_GETHOSTBYNAME
static ul_i
_fetch_addr(char const *host, struct dns_leapinfo *dlp)
{
	struct addrinfo hints, *res;
	int i;
	ul_i rv = (ul_i)-1;

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	res = NULL;

	if ((i = getaddrinfo(host, NULL, &hints, &res)) != 0) {
		fprintf(stderr, "! Cannot resolve `%s': %s\n",
			host, gai_strerror(i));
		goto jleave;
	}

	if ((i = getnameinfo(res->ai_addr, res->ai_addrlen,
			dlp->dl_addr, sizeof dlp->dl_addr,
			NULL, 0, NI_NUMERICHOST)) != 0) {
		fprintf(stderr, "! Cannot resolve name `%s': %s\n",
			host, gai_strerror(i));
		goto jleave;
	}

	rv = inet_addr(dlp->dl_addr);
jleave:
	if (res != NULL)
		freeaddrinfo(res);
	return rv;
}

#else /* !WANT_GETHOSTBYNAME */
static ul_i
_fetch_addr(char const *host, struct dns_leapinfo *dlp)
{
	struct hostent *hp;
	struct in_addr *iaddr;
	char const *saddr;
	ul_i rv = (ul_i)-1;

	if ((hp = gethostbyname(host)) == NULL) {
		char const *emsg;

		switch (h_errno) {
		case HOST_NOT_FOUND:
			emsg = "host not found";
			break;
		default:
		case TRY_AGAIN:
			emsg = "(maybe) try again later";
			break;
		case NO_RECOVERY:
			emsg = "non-recoverable server error";
			break;
		case NO_DATA:
			emsg = "valid name without IP address";
			break;
		}
		fprintf(stderr, "! Cannot resolve `%s': %s\n", host, emsg);
		goto jleave;
	}

	if (hp->h_addrtype != AF_INET) {
		fprintf(stderr, "! DNS didn't return IPv4 address for `%s'\n",
			host);
		goto jleave;
	}

	/* (Don't check h_length etc..) */
	iaddr = (struct in_addr*)hp->h_addr_list[0];
	saddr = inet_ntoa(*iaddr);
	strncpy(dlp->dl_addr, saddr, sizeof dlp->dl_addr);
	rv = inet_addr(saddr);
jleave:
	return rv;
}
#endif /* WANT_GETHOSTBYNAME */

static bool_t
_dl_addr_to_aparts(struct dns_leapinfo *dlp)
{
	ul_i i;
	char *eptr;
	char const *sptr = dlp->dl_addr;
	bool_t rv = FAL0;

	for (i = 0;; ++i) {
		dlp->dl_addr_parts[i] = strtoul(sptr, &eptr, 10);
		if (sptr == eptr)
			goto jleave;
		switch (*eptr) {
		case '\0':
			if (i == 3)
				rv = TRU1;
			goto jleave;
		case '.':
			if (i == 3)
				goto jleave;
			sptr = eptr + 1;
			break;
		default:
			goto jleave;
		}
	}
	rv = TRU1;
jleave:
	return rv;
}

static bool_t
_dl_crc8_phk(struct dns_leapinfo const *dlp)
{
	ul_i const poly = 0xCF;
	ul_i crc, i, slot, j, mix;

	for (crc = i = 0; i < NELEM(dlp->dl_addr_parts); ++i)
		for (slot = dlp->dl_addr_parts[i], j = 0; j < 8; ++j) {
			mix = (crc ^ slot) & 0x01;
			crc >>= 1;
			if (mix != 0)
				crc ^= poly;
			slot >>= 1;
		}
	return (crc == 0 ? TRU1 : FAL0);
}

static bool_t
_dl_explode(struct dns_leapinfo *dlp)
{
	ul_i i;
	bool_t rv = FAL0;

	i = dlp->dl_addr_parts[0] << 16;
	i |= dlp->dl_addr_parts[1] << 8;
	i |= dlp->dl_addr_parts[2];

	/* Must be class E address */
	if ((i & 0xF00000) != 0xF00000)
		goto jleave;
	i &= ~0xF00000;

	switch ((dlp->dl_adjust = (sc_i)(i & 0x03))) {
	case 0x00:
	case 0x02:
		goto jleave;
	case 0x03:
		dlp->dl_adjust = -1;
	default:
		break;
	}
	i >>= 2;
	dlp->dl_drift = (sc_i)(i & 0xFF);
	i >>= 8;
	dlp->dl_month = (uc_i)(i % 12) + 1;
	dlp->dl_year = (us_i)(i / 12) + 1972;

	rv = TRU1;
jleave:
	return rv;
}
_______________________________________________
LEAPSECS mailing list
[email protected]
https://pairlist6.pair.net/mailman/listinfo/leapsecs

Reply via email to