i wrote:
 |Below a simple C version for the interested.  It doesn't iterate

just an update with encode mode and different integer types (and
ooops bug fixes: accept a "0" adjustment and don't print print two
hyphens for negative drifts).
I wonder wether the drift shouldn't be made unsigned.
Juhuuuu,

--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:
 *@   Fetching: $ ./leapsec SERVER
 *@     Encode: $ ./leapsec YEAR MONTH BASE-DRIFT ADJUSTMENT
 *
 * Public Domain.
 */

#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

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

#if defined __STDC_VERSION__ && __STDC_VERSION__ + 0 >= 199901L
# include <stdint.h>
#else
# include <inttypes.h>
#endif

#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

#ifdef UINT8_MAX
typedef uint8_t           ui8_t;
typedef int8_t            si8_t;
#elif UCHAR_MAX != 255
# error UCHAR_MAX must be 255
#else
typedef unsigned char     ui8_t;
typedef signed char       si8_t;
#endif

#ifdef UINT16_MAX
typedef uint16_t          ui16_t;
typedef int16_t           si16_t;
#elif USHRT_MAX != 0xFFFFu
# error USHRT_MAX must be 0xFFFF
#else
typedef unsigned short    ui16_t;
typedef signed short      si16_t;
#endif

#ifdef UINT32_MAX
typedef uint32_t          ui32_t;
typedef int32_t           si32_t;
#elif ULONG_MAX == 0xFFFFFFFFu
typedef unsigned long int ui32_t;
typedef signed long int   si32_t;
#elif UINT_MAX != 0xFFFFFFFFu
# error UINT_MAX must be 0xFFFFFFFF
#else
typedef unsigned int      ui32_t;
typedef signed int        si32_t;
#endif

typedef enum {FAL0, TRU1} bool_t;

/*
 * PHK DNS A:
 * - Class E: 4-bit (0b1111)
 * - Months since 1972: 10-bit
 * - Base drift before leapsecond: 8-bit
 * - Drift adjustment: 2-bit
 * - CRC-8 PHK checksum: 8-bit
 */
struct dns_leapinfo {
  ui32_t  dl_addr_parts[4];
  ui8_t   _dl_pad1[1];
  si8_t   dl_adjust;
  si8_t   dl_drift;
  ui8_t   dl_month;
  ui16_t  dl_year;
  ui8_t   _dl_pad2[2];
  char    dl_addr[NI_MAXHOST];
};

static int    _encode(char **argv, struct dns_leapinfo *dlp);

static bool_t _fetch_addr(char const *host, struct dns_leapinfo *dlp);
static bool_t _dl_addr_to_aparts(struct dns_leapinfo *dlp);
/* count specifies the number of array entries to be walked */
static ui32_t _dl_crc8_phk(struct dns_leapinfo const *dlp, ui32_t count);
static bool_t _dl_explode(struct dns_leapinfo *dlp);

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

  rv = _EX_USAGE;
  if (argc != 2 && argc != 5) {
    fprintf(stderr,
      "Synopsis fetch : %s SERVER\n"
      "         encode: %s YEAR MONTH BASE-DRIFT ADJUSTMENT\n",
      argv[0], argv[0]);
    goto jleave;
  }

  if (argc == 5)
    argc = _encode(argv + 1, &dl);
  else {
    argc = _EX_OK;
    rv = _EX_NOHOST;
    if (!_fetch_addr(argv[1], &dl)) {
      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, NELEM(dl.dl_addr_parts)) != 0) {
    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 = (argc != _EX_OK) ? argc : _EX_OK;
jprint:
  printf("%s -> %s %04u-%02u %s%d %s%d\n",
    dl.dl_addr, (rv == _EX_OK ? "OK" : "BAD"),
    (ui32_t)dl.dl_year, (ui32_t)dl.dl_month,
    (dl.dl_drift < 0 ? "" : "+"), (si32_t)dl.dl_drift,
    (dl.dl_adjust < 0 ? "" : "+"), (si32_t)dl.dl_adjust);
jleave:
  return rv;
}

static int
_encode(char **argv, struct dns_leapinfo *dlp)
{
  ui32_t y, m, i;
  si32_t drift, adj;
  int rv = _EX_OK;

  /* Regarding strto*() handling */
  printf("Warning: encode mode doesn't truly verify arguments!\n");

  y = (ui32_t)strtoul(argv[0], NULL, 0);
  if (y < 1972 || y > 2057) {
    fprintf(stderr, "! Invalid (non-representable) year: %u\n", y);
    rv = _EX_DATAERR;
  }
  m = (ui32_t)strtoul(argv[1], NULL, 0);
  if (m < 1 || m > 12 || (y == 2057 && m > 4)) {
    fprintf(stderr, "! Invalid (non-representable) month %u (for year %u)\n",
      m, y);
    rv = _EX_DATAERR;
  }
  drift = (si32_t)strtol(argv[2], NULL, 0);
  if (drift < -128 || drift > 127) {
    fprintf(stderr, "! Invalid (non-representable) drift: %d\n", drift);
    rv = _EX_DATAERR;
  }
  adj = (si32_t)strtol(argv[3], NULL, 0);
  if (adj < -1 || adj > 1 || adj == 0) {
    fprintf(stderr, "! Invalid adjustment: %d\n", adj);
    rv = _EX_DATAERR;
  }

  i = (y - 1972) * 12 + (m - 1);
  i <<= 8;
  i |= drift;
  i <<= 2;
  switch (adj) {
  case  1: i |= 0x1; break;
  case -1: i |= 0x3; break;
  default: break;
  }
  i <<= 8;
  i |= 0xF0000000u;

  dlp->dl_addr_parts[0] = i >> 24;
  dlp->dl_addr_parts[1] = (i >> 16) & 0xFF;
  dlp->dl_addr_parts[2] = (i >>  8) & 0xFF;
  dlp->dl_addr_parts[3] = _dl_crc8_phk(dlp, NELEM(dlp->dl_addr_parts) - 1);

#if defined __STDC_VERSION__ && __STDC_VERSION__ + 0 >= 199901L
  snprintf
#else
  sprintf
#endif
    (dlp->dl_addr,
#if defined __STDC_VERSION__ && __STDC_VERSION__ + 0 >= 199901L
      sizeof dlp->dl_addr,
#endif
    "%u.%u.%u.%u",
    dlp->dl_addr_parts[0], dlp->dl_addr_parts[1], dlp->dl_addr_parts[2],
    dlp->dl_addr_parts[3]
  );
  return rv;
}

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

  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 = TRU1;
jleave:
  if (res != NULL)
    freeaddrinfo(res);
  return rv;
}

#else /* !WANT_GETHOSTBYNAME */
static bool_t
_fetch_addr(char const *host, struct dns_leapinfo *dlp)
{
  struct hostent *hp;
  struct in_addr *iaddr;
  char const *saddr;
  size_t i;
  bool_t rv = FAL0;

  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);

  i = strlen(saddr) +1;
  if (i <= sizeof dlp->dl_addr) {
    memcpy(dlp->dl_addr, saddr, i);
    rv = TRU1;
  }
jleave:
  return rv;
}
#endif /* WANT_GETHOSTBYNAME */

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

  for (i = 0;; ++i) {
    unsigned long l = strtoul(sptr, &eptr, 10);
    if (l != (ui32_t)l)
      goto jleave;
    dlp->dl_addr_parts[i] = (ui32_t)l;

    if (sptr == eptr)
      goto jleave;
    switch (*eptr) {
    case '.':
      if (i == 3)
        goto jleave;
      sptr = eptr + 1;
      break;
    case '\0':
      if (i == 3)
        rv = TRU1;
      /* FALLTHRU */
    default:
      goto jleave;
    }
  }

  rv = TRU1;
jleave:
  return rv;
}

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

  for (crc = i = 0; i < count; ++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;
}

static bool_t
_dl_explode(struct dns_leapinfo *dlp)
{
  ui32_t 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 = (si8_t)(i & 0x03))) {
  case 0x02:
    goto jleave;
  case 0x03:
    dlp->dl_adjust = -1;
  case 0x00:
  default:
    break;
  }
  i >>= 2;
  dlp->dl_drift = (si8_t)(i & 0xFF);
  i >>= 8;
  dlp->dl_month = (ui8_t)(i % 12) + 1;
  dlp->dl_year = (ui16_t)(i / 12) + 1972;

  rv = TRU1;
jleave:
  return rv;
}

/* s-it2-mode */
_______________________________________________
LEAPSECS mailing list
[email protected]
https://pairlist6.pair.net/mailman/listinfo/leapsecs

Reply via email to