Goto sanos source index

//
// sntp.c
//
// Simple Network Time Protocol
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <string.h>
#include <inifile.h>

#define NTP_EPOCH            (86400U * (365U * 70U + 17U))
#define NTP_PORT             123
#define MAX_NTP_SERVERS      16
#define NTP_REPLY_TIMEOUT    6000
#define TIME_ADJUST_INTERVAL (8 * 60 * 60 * 1000)
#define TIME_ADJUST_RETRY    (5 * 60 * 1000)

struct ntp_server {
  struct sockaddr_in sa;
  char *hostname;
};

struct ntp_packet {
  unsigned char mode_vn_li;
  unsigned char stratum;
  char poll;
  char precision;
  unsigned long root_delay;
  unsigned long root_dispersion;
  unsigned long reference_identifier;
  unsigned long reference_timestamp_secs;
  unsigned long reference_timestamp_fraq;
  unsigned long originate_timestamp_secs;
  unsigned long originate_timestamp_fraq;
  unsigned long receive_timestamp_seqs;
  unsigned long receive_timestamp_fraq;
  unsigned long transmit_timestamp_secs;
  unsigned long transmit_timestamp_fraq;
};

static struct ntp_server ntp_servers[MAX_NTP_SERVERS];
static int num_ntp_servers;

int sntp_get(struct ntp_server *srv, struct timeval *tv) {
  struct ntp_packet pkt;
  int s;
  int rc;
  int timeout;

  //syslog(LOG_AUX, "sntp_get: retrieving time from %a", &srv->sa.sin_addr);

  s = socket(AF_INET, SOCK_DGRAM, 0);
  if (s < 0) return s;

  rc = connect(s, (struct sockaddr *) &srv->sa, sizeof(struct sockaddr_in));
  if (rc < 0) {
    close(s);
    return rc;
  }

  memset(&pkt, 0, sizeof pkt);
  pkt.mode_vn_li = (4 << 3) | 3;
  pkt.originate_timestamp_secs = htonl(time(0) + NTP_EPOCH);

  rc = send(s, &pkt, sizeof pkt, 0);
  if (rc != sizeof pkt) {
    close(s);
    return rc;
  }

  timeout = NTP_REPLY_TIMEOUT;
  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(int)); 

  rc = recvfrom(s, &pkt, sizeof pkt, 0, NULL, NULL);
  if (rc != sizeof pkt) {
    close(s);
    return rc;
  }

  tv->tv_sec = ntohl(pkt.transmit_timestamp_secs) - NTP_EPOCH;
  tv->tv_usec = ntohl(pkt.transmit_timestamp_fraq) / 4295;

  close(s);
  return 0;
}

void __stdcall sntpd(void *arg) {
  int i, j;
  struct ntp_server *srv;
  struct hostent *hp;
  int success;
  struct timeval tv;

  //syslog(LOG_AUX, "sntpd: started");
  while (1) {
    success = 0;
    for (i = 0; i < num_ntp_servers; i++) {
      srv = &ntp_servers[i];

      if (srv->hostname != NULL) {
        hp = gethostbyname(srv->hostname);
        if (hp) {
          for (j = 0; hp->h_addr_list[j] != NULL; j++) {
            struct in_addr *addr = (struct in_addr *) (hp->h_addr_list[j]);
            memcpy(&srv->sa.sin_addr, addr, hp->h_length);
            if (sntp_get(srv, &tv) >= 0) {
              success = 1;
              break;
            }
          }
        }
      } else {
        if (sntp_get(srv, &tv) >= 0) success = 1;
      }

      if (success) break;
    }

    if (success) {
      //struct timeval now;
      //gettimeofday(&now);
      settimeofday(&tv);
      //syslog(LOG_AUX, "sntpd: adjusting %d %d %a", tv.tv_sec - now.tv_sec, tv.tv_usec - now.tv_usec, &srv->sa.sin_addr);
      msleep(TIME_ADJUST_INTERVAL);
    } else {
      syslog(LOG_AUX, "sntpd: error obtaining time from time server");
      msleep(TIME_ADJUST_RETRY);
    }
  }
}

void init_sntpd() {
  int idx;
  struct section *sect;
  struct property *prop;
  struct peb *peb = getpeb();
  
  if (peb->ipaddr.s_addr == INADDR_ANY) return;

  idx = 0;
  if (peb->ntp_server1.s_addr != INADDR_ANY) {
    ntp_servers[idx].hostname = NULL;
    ntp_servers[idx].sa.sin_addr.s_addr = peb->ntp_server1.s_addr;
    ntp_servers[idx].sa.sin_family = AF_INET;
    ntp_servers[idx].sa.sin_port = htons(NTP_PORT);
    idx++;
  }

  if (peb->ntp_server2.s_addr != INADDR_ANY) {
    ntp_servers[idx].hostname = NULL;
    ntp_servers[idx].sa.sin_addr.s_addr = peb->ntp_server2.s_addr;
    ntp_servers[idx].sa.sin_family = AF_INET;
    ntp_servers[idx].sa.sin_port = htons(NTP_PORT);
    idx++;
  }

  sect = find_section(osconfig(), "ntp");
  if (sect) {
    prop = sect->properties;
    while (prop) {
      if (idx == MAX_NTP_SERVERS) break;

      ntp_servers[idx].hostname = prop->name;
      ntp_servers[idx].sa.sin_addr.s_addr = INADDR_ANY;
      ntp_servers[idx].sa.sin_family = AF_INET;
      ntp_servers[idx].sa.sin_port = htons(NTP_PORT);

      idx++;
      prop = prop->next;
    }
  }

  num_ntp_servers = idx;
  if (idx > 0) beginthread(sntpd, 0, NULL, 0, "sntpd", NULL);
}