static void
free_res(
	restrict_u *	res,
	int		v6
	)
{
	restrict_u **	plisthead;
	restrict_u *	unlinked;

	restrictcount--;
	if (RES_LIMITED & res->flags)
		dec_res_limited();

	if (v6)
		plisthead = &restrictlist6;
	else
		plisthead = &restrictlist4;
	UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u);
	NTP_INSIST(unlinked == res);

	if (v6) {
		zero_mem(res, V6_SIZEOF_RESTRICT_U);
		plisthead = &resfree6;
	} else {
		zero_mem(res, V4_SIZEOF_RESTRICT_U);
		plisthead = &resfree4;
	}
	LINK_SLIST(*plisthead, res, link);
}
Exemple #2
0
uint32_t
caltontp(
	const struct calendar *jt
	)
{
	int32_t eraday;	/* CE Rata Die number	*/
	vint64  ntptime;/* resulting NTP time	*/

	NTP_INSIST(jt != NULL);

	NTP_REQUIRE(jt->month <= 13);	/* permit month 0..13! */
	NTP_REQUIRE(jt->monthday <= 32);
	NTP_REQUIRE(jt->yearday <= 366);
	NTP_REQUIRE(jt->hour <= 24);
	NTP_REQUIRE(jt->minute <= MINSPERHR);
	NTP_REQUIRE(jt->second <= SECSPERMIN);

	/*
	 * First convert the date to he corresponding RataDie
	 * number. If yearday is not zero, assume that it contains a
	 * useable value and avoid all calculations involving month
	 * and day-of-month. Do a full evaluation otherwise.
	 */
	if (jt->yearday)
		eraday = ntpcal_year_to_ystart(jt->year)
		       + jt->yearday - 1;
	else
		eraday = ntpcal_date_to_rd(jt);

	ntptime = ntpcal_dayjoin(eraday - DAY_NTP_STARTS,
				 ntpcal_etime_to_seconds(jt->hour, jt->minute,
							 jt->second));
	return ntptime.d_s.lo;
}
static restrict_u *
match_restrict6_addr(
	const struct in6_addr *	addr,
	u_short			port
	)
{
	const int	v6 = 1;
	restrict_u *	res;
	restrict_u *	next;
	struct in6_addr	masked;

	for (res = restrictlist6; res != NULL; res = next) {
		next = res->link;
		NTP_INSIST(next != res);
		if (res->expire &&
		    res->expire <= current_time)
			free_res(res, v6);
		MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask);
		if (ADDR6_EQ(&masked, &res->u.v6.addr)
		    && (!(RESM_NTPONLY & res->mflags)
			|| NTP_PORT == (int)port))
			break;
	}
	return res;
}
Exemple #4
0
/*
 * refclock_open - open serial port for reference clock
 *
 * This routine opens a serial port for I/O and sets default options. It
 * returns the file descriptor if success and zero if failure.
 */
int
refclock_open(
	char	*dev,		/* device name pointer */
	u_int	speed,		/* serial port speed (code) */
	u_int	lflags		/* line discipline flags */
	)
{
	int	fd;
	int	omode;
#ifdef O_NONBLOCK
	char    trash[128];	/* litter bin for old input data */
#endif

	/*
	 * Open serial port and set default options
	 */
	omode = O_RDWR;
#ifdef O_NONBLOCK
	omode |= O_NONBLOCK;
#endif
#ifdef O_NOCTTY
	omode |= O_NOCTTY;
#endif

	fd = open(dev, omode, 0777);
	if (fd < 0) {
		msyslog(LOG_ERR, "refclock_open %s: %m", dev);
		return (0);
	}
	NTP_INSIST(fd != 0);
	if (!refclock_setup(fd, speed, lflags)) {
		close(fd);
		return (0);
	}
	if (!refclock_ioctl(fd, lflags)) {
		close(fd);
		return (0);
	}
#ifdef O_NONBLOCK
	/*
	 * We want to make sure there is no pending trash in the input
	 * buffer. Since we have non-blocking IO available, this is a
	 * good moment to read and dump all available outdated stuff
	 * that might have become toxic for the driver.
	 */
	while (read(fd, trash, sizeof(trash)) > 0 || errno == EINTR)
		/*NOP*/;
#endif
	return (fd);
}
Exemple #5
0
/*
 * getnetnum - given a host name, return its net number
 *	       and (optional) full name
 */
static int
getnetnum(
	const char *hname,
	sockaddr_u *num,
	char *fullhost,
	int af
	)
{
	struct addrinfo hints, *ai = NULL;

	ZERO(hints);
	hints.ai_flags = AI_CANONNAME;
#ifdef AI_ADDRCONFIG
	hints.ai_flags |= AI_ADDRCONFIG;
#endif
	
	/*
	 * decodenetnum only works with addresses, but handles syntax
	 * that getaddrinfo doesn't:  [2001::1]:1234
	 */
	if (decodenetnum(hname, num)) {
		if (fullhost != NULL)
			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
				    LENHOSTNAME, NULL, 0, 0);
		return 1;
	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
		NTP_INSIST(sizeof(*num) >= ai->ai_addrlen);
		memcpy(num, ai->ai_addr, ai->ai_addrlen);
		if (fullhost != NULL) {
			if (ai->ai_canonname != NULL)
				strlcpy(fullhost, ai->ai_canonname,
					LENHOSTNAME);
			else
				getnameinfo(&num->sa, SOCKLEN(num),
					    fullhost, LENHOSTNAME, NULL,
					    0, 0);
		}
		return 1;
	}
	fprintf(stderr, "***Can't find host %s\n", hname);

	return 0;
}
Exemple #6
0
void
caljulian(
	uint32_t		ntp,
	struct calendar *	jt
	)
{
	vint64		vlong;
	ntpcal_split	split;
	
	
	NTP_INSIST(NULL != jt);

	/*
	 * Unfold ntp time around current time into NTP domain. Split
	 * into days and seconds, shift days into CE domain and
	 * process the parts.
	 */
	vlong = ntpcal_ntp_to_ntp(ntp, NULL);
	split = ntpcal_daysplit(&vlong);
	ntpcal_daysplit_to_date(jt, &split, DAY_NTP_STARTS);
}
static restrict_u *
alloc_res6(void)
{
	const size_t	cb = V6_SIZEOF_RESTRICT_U;
	const size_t	count = INC_RESLIST6;
	restrict_u *	rl;
	restrict_u *	res;
	int		i;

	UNLINK_HEAD_SLIST(res, resfree6, link);
	if (res != NULL)
		return res;

	rl = emalloc_zero(count * cb);
	/* link all but the first onto free list */
	res = (void *)((char *)rl + (count - 1) * cb);
	for (i = count - 1; i > 0; i--) {
		LINK_SLIST(resfree6, res, link);
		res = (void *)((char *)res - cb);
	}
	NTP_INSIST(rl == res);
	/* allocate the first */
	return res;
}
Exemple #8
0
static struct tm *
get_struct_tm(
	const vint64 *stamp,
	int	      local)
{
	struct tm *tm	 = NULL;
	int32	   folds = 0;
	time_t	   ts;

#ifdef HAVE_INT64

	int64 tl;
	ts = tl = stamp->q_s;

	/*
	 * If there is chance of truncation, try to fix it. Let the
	 * compiler find out if this can happen at all.
	 */
	while (ts != tl) { /* truncation? */
		if (tl < 0) {
			if (--folds < MINFOLD)
				return NULL;
			tl += SOLAR_CYCLE_SECS;
		} else {
			if (++folds > MAXFOLD)
				return NULL;
			tl -= SOLAR_CYCLE_SECS;
		}
		ts = tl; /* next try... */
	}
#else

	/*
	 * since we do not have 64-bit scalars, it's not likely we have
	 * 64-bit time_t. Assume 32 bits and properly reduce the value.
	 */
	u_int32 hi, lo;

	hi = stamp->D_s.hi;
	lo = stamp->D_s.lo;

	while ((hi && ~hi) || ((hi ^ lo) & 0x80000000u)) {
		if (M_ISNEG(hi, lo)) {
			if (--folds < MINFOLD)
				return NULL;
			M_ADD(hi, lo, 0, SOLAR_CYCLE_SECS);
		} else {
			if (++folds > MAXFOLD)
				return NULL;
			M_SUB(hi, lo, 0, SOLAR_CYCLE_SECS);
		}
	}
	ts = (int32)lo;

#endif

	/*
	 * 'ts' should be a suitable value by now. Just go ahead, but
	 * with care:
	 *
	 * There are some pathological implementations of 'gmtime()'
	 * and 'localtime()' out there. No matter if we have 32-bit or
	 * 64-bit 'time_t', try to fix this by solar cycle warping
	 * again...
	 *
	 * At least the MSDN says that the (Microsoft) Windoze
	 * versions of 'gmtime()' and 'localtime()' will bark on time
	 * stamps < 0.
	 */
	while ((tm = (*(local ? localtime : gmtime))(&ts)) == NULL)
		if (ts < 0) {
			if (--folds < MINFOLD)
				return NULL;
			ts += SOLAR_CYCLE_SECS;
		} else if (ts >= (time_t)SOLAR_CYCLE_SECS) {
			if (++folds > MAXFOLD)
				return NULL;
			ts -= SOLAR_CYCLE_SECS;
		} else
			return NULL; /* That's truly pathological! */

	/* 'tm' surely not NULL here! */
	NTP_INSIST(tm != NULL);
	if (folds != 0) {
		tm->tm_year += folds * SOLAR_CYCLE_YEARS;
		if (tm->tm_year <= 0 || tm->tm_year >= 200)
			return NULL;	/* left warp range... can't help here! */
	}

	return tm;
}
Exemple #9
0
const char *
socktohost(
	const sockaddr_u *sock
	)
{
	const char		svc[] = "ntp";
	char *			pbuf;
	char *			pliar;
	int			gni_flags;
	struct addrinfo		hints;
	struct addrinfo *	alist;
	struct addrinfo *	ai;
	sockaddr_u		addr;
	size_t			octets;
	int			a_info;

	/* reverse the address to purported DNS name */
	LIB_GETBUF(pbuf);
	gni_flags = NI_DGRAM | NI_NAMEREQD;
	if (getnameinfo(&sock->sa, SOCKLEN(sock), pbuf, LIB_BUFLENGTH,
			NULL, 0, gni_flags))
		return stoa(sock);	/* use address */

	TRACE(1, ("%s reversed to %s\n", stoa(sock), pbuf));

	/*
	 * Resolve the reversed name and make sure the reversed address
	 * is among the results.
	 */
	ZERO(hints);
	hints.ai_family = AF(sock);
	hints.ai_protocol = IPPROTO_UDP;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_flags = 0;
	alist = NULL;

	a_info = getaddrinfo(pbuf, svc, &hints, &alist);
	if (a_info == EAI_NONAME
#ifdef EAI_NODATA
	    || a_info == EAI_NODATA
#endif
	   ) {
		hints.ai_flags = AI_CANONNAME;
#ifdef AI_ADDRCONFIG
		hints.ai_flags |= AI_ADDRCONFIG;
#endif
		a_info = getaddrinfo(pbuf, svc, &hints, &alist);	
	}
#ifdef AI_ADDRCONFIG
	/* Some older implementations don't like AI_ADDRCONFIG. */
	if (a_info == EAI_BADFLAGS) {
		hints.ai_flags &= ~AI_ADDRCONFIG;
		a_info = getaddrinfo(pbuf, svc, &hints, &alist);	
	}
#endif
	if (a_info)
		goto forward_fail;

	NTP_INSIST(alist != NULL);

	for (ai = alist; ai != NULL; ai = ai->ai_next) {
		/*
		 * Make a convenience sockaddr_u copy from ai->ai_addr
		 * because casting from sockaddr * to sockaddr_u * is
		 * risking alignment problems on platforms where
		 * sockaddr_u has stricter alignment than sockaddr,
		 * such as sparc.
		 */
		ZERO_SOCK(&addr);
		octets = min(sizeof(addr), ai->ai_addrlen);
		memcpy(&addr, ai->ai_addr, octets);
		if (SOCK_EQ(sock, &addr))
			break;
	}
	freeaddrinfo(alist);

	if (ai != NULL)
		return pbuf;	/* forward check passed */

    forward_fail:
	TRACE(1, ("%s forward check lookup fail: %s\n", pbuf,
		  gai_strerror(a_info)));
	LIB_GETBUF(pliar);
	snprintf(pliar, LIB_BUFLENGTH, "%s (%s)", stoa(sock), pbuf);

	return pliar;
}
Exemple #10
0
/*
 * openhost - open a socket to a host
 */
static int
openhost(
	const char *hname
	)
{
	char temphost[LENHOSTNAME];
	int a_info, i;
	struct addrinfo hints, *ai = NULL;
	register const char *cp;
	char name[LENHOSTNAME];
	char service[5];

	/*
	 * We need to get by the [] if they were entered 
	 */
	
	cp = hname;
	
	if (*cp == '[') {
		cp++;	
		for (i = 0; *cp && *cp != ']'; cp++, i++)
			name[i] = *cp;
		if (*cp == ']') {
			name[i] = '\0';
			hname = name;
		} else {
			return 0;
		}
	}	

	/*
	 * First try to resolve it as an ip address and if that fails,
	 * do a fullblown (dns) lookup. That way we only use the dns
	 * when it is needed and work around some implementations that
	 * will return an "IPv4-mapped IPv6 address" address if you
	 * give it an IPv4 address to lookup.
	 */
	strcpy(service, "ntp");
	memset((char *)&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = ai_fam_templ;
	hints.ai_protocol = IPPROTO_UDP;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_flags = AI_NUMERICHOST;

	a_info = getaddrinfo(hname, service, &hints, &ai);
	if (a_info == EAI_NONAME
#ifdef EAI_NODATA
	    || a_info == EAI_NODATA
#endif
	   ) {
		hints.ai_flags = AI_CANONNAME;
#ifdef AI_ADDRCONFIG
		hints.ai_flags |= AI_ADDRCONFIG;
#endif
		a_info = getaddrinfo(hname, service, &hints, &ai);	
	}
	/* Some older implementations don't like AI_ADDRCONFIG. */
	if (a_info == EAI_BADFLAGS) {
		hints.ai_flags = AI_CANONNAME;
		a_info = getaddrinfo(hname, service, &hints, &ai);	
	}
	if (a_info != 0) {
		(void) fprintf(stderr, "%s\n", gai_strerror(a_info));
		if (ai != NULL)
			freeaddrinfo(ai);
		return 0;
	}

	/* 
	 * getaddrinfo() has returned without error so ai should not 
	 * be NULL.
	 */
	NTP_INSIST(ai != NULL);

	if (ai->ai_canonname == NULL) {
		strncpy(temphost, stoa((sockaddr_u *)ai->ai_addr),
		    LENHOSTNAME);
		temphost[LENHOSTNAME-1] = '\0';
	} else {
		strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
		temphost[LENHOSTNAME-1] = '\0';
	}

	if (debug > 2)
	    printf("Opening host %s\n", temphost);

	if (havehost == 1) {
		if (debug > 2)
		    printf("Closing old host %s\n", currenthost);
		(void) closesocket(sockfd);
		havehost = 0;
	}
	(void) strcpy(currenthost, temphost);
	
	/* port maps to the same in both families */
	s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port; 
#ifdef SYS_VXWORKS
	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
	if (ai->ai_family == AF_INET)
		*(struct sockaddr_in *)&hostaddr= 
			*((struct sockaddr_in *)ai->ai_addr);
	else 
		*(struct sockaddr_in6 *)&hostaddr= 
			*((struct sockaddr_in6 *)ai->ai_addr);
#endif /* SYS_VXWORKS */

#ifdef SYS_WINNT
	{
		int optionValue = SO_SYNCHRONOUS_NONALERT;
		int err;

		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
		if (err != NO_ERROR) {
			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
			exit(1);
		}
	}

	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
	if (sockfd == INVALID_SOCKET) {
		error("socket", "", "");
		exit(-1);
	}
#else
	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
	if (sockfd == -1)
	    error("socket", "", "");
#endif /* SYS_WINNT */

	
#ifdef NEED_RCVBUF_SLOP
# ifdef SO_RCVBUF
	{
		int rbufsize = INITDATASIZE + 2048; /* 2K for slop */

		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
			       &rbufsize, sizeof(int)) == -1)
		    error("setsockopt", "", "");
	}
# endif
#endif

#ifdef SYS_VXWORKS
	if (connect(sockfd, (struct sockaddr *)&hostaddr, 
		    sizeof(hostaddr)) == -1)
#else
	if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
		    ai->ai_addrlen) == -1)
#endif /* SYS_VXWORKS */
	    error("connect", "", "");

	freeaddrinfo(ai);
	havehost = 1;
	req_pkt_size = REQ_LEN_NOMAC;
	impl_ver = IMPL_XNTPD;
	return 1;
}
/*
 * hack_restrict - add/subtract/manipulate entries on the restrict list
 */
void
hack_restrict(
	int		op,
	sockaddr_u *	resaddr,
	sockaddr_u *	resmask,
	u_short		mflags,
	u_short		flags,
	u_long		expire
	)
{
	int		v6;
	restrict_u	match;
	restrict_u *	res;
	restrict_u **	plisthead;

	DPRINTF(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n",
		    op, stoa(resaddr), stoa(resmask), mflags, flags));

	if (NULL == resaddr) {
		NTP_REQUIRE(NULL == resmask);
		NTP_REQUIRE(RESTRICT_FLAGS == op);
		restrict_source_flags = flags;
		restrict_source_mflags = mflags;
		restrict_source_enabled = 1;
		return;
	}

	ZERO(match);
	/* silence VC9 potentially uninit warnings */
	res = NULL;
	v6 = 0;

	if (IS_IPV4(resaddr)) {
		v6 = 0;
		/*
		 * Get address and mask in host byte order for easy
		 * comparison as u_int32
		 */
		match.u.v4.addr = SRCADR(resaddr);
		match.u.v4.mask = SRCADR(resmask);
		match.u.v4.addr &= match.u.v4.mask;

	} else if (IS_IPV6(resaddr)) {
		v6 = 1;
		/*
		 * Get address and mask in network byte order for easy
		 * comparison as byte sequences (e.g. memcmp())
		 */
		match.u.v6.mask = SOCK_ADDR6(resmask);
		MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr),
			       &match.u.v6.mask);

	} else	/* not IPv4 nor IPv6 */
		NTP_REQUIRE(0);

	match.flags = flags;
	match.mflags = mflags;
	match.expire = expire;
	res = match_restrict_entry(&match, v6);

	switch (op) {

	case RESTRICT_FLAGS:
		/*
		 * Here we add bits to the flags. If this is a
		 * new restriction add it.
		 */
		if (NULL == res) {
			if (v6) {
				res = alloc_res6();
				memcpy(res, &match,
				       V6_SIZEOF_RESTRICT_U);
				plisthead = &restrictlist6;
			} else {
				res = alloc_res4();
				memcpy(res, &match,
				       V4_SIZEOF_RESTRICT_U);
				plisthead = &restrictlist4;
			}
			LINK_SORT_SLIST(
				*plisthead, res,
				(v6)
				  ? res_sorts_before6(res, L_S_S_CUR())
				  : res_sorts_before4(res, L_S_S_CUR()),
				link, restrict_u);
			restrictcount++;
			if (RES_LIMITED & flags)
				inc_res_limited();
		} else {
			if ((RES_LIMITED & flags) &&
			    !(RES_LIMITED & res->flags))
				inc_res_limited();
			res->flags |= flags;
		}
		break;

	case RESTRICT_UNFLAG:
		/*
		 * Remove some bits from the flags. If we didn't
		 * find this one, just return.
		 */
		if (res != NULL) {
			if ((RES_LIMITED & res->flags)
			    && (RES_LIMITED & flags))
				dec_res_limited();
			res->flags &= ~flags;
		}
		break;

	case RESTRICT_REMOVE:
	case RESTRICT_REMOVEIF:
		/*
		 * Remove an entry from the table entirely if we
		 * found one. Don't remove the default entry and
		 * don't remove an interface entry.
		 */
		if (res != NULL
		    && (RESTRICT_REMOVEIF == op
			|| !(RESM_INTERFACE & res->mflags))
		    && res != &restrict_def4
		    && res != &restrict_def6)
			free_res(res, v6);
		break;

	default:	/* unknown op */
		NTP_INSIST(0);
		break;
	}

}
Exemple #12
0
/*
 * decodenetnum		convert text IP address and port to sockaddr_u
 *
 * Returns 0 for failure, 1 for success.
 */
int
decodenetnum(
	const char *num,
	sockaddr_u *netnum
	)
{
	struct addrinfo hints, *ai = NULL;
	int err;
	u_short port;
	const char *cp;
	const char *port_str;
	char *pp;
	char *np;
	char name[80];

	NTP_REQUIRE(num != NULL);
	NTP_REQUIRE(strlen(num) < sizeof(name));

	port_str = NULL;
	if ('[' != num[0]) {
		/*
		 * to distinguish IPv6 embedded colons from a port
		 * specification on an IPv4 address, assume all 
		 * legal IPv6 addresses have at least two colons.
		 */
		pp = strchr(num, ':');
		if (NULL == pp)
			cp = num;	/* no colons */
		else if (NULL != strchr(pp + 1, ':'))
			cp = num;	/* two or more colons */
		else {			/* one colon */
			strlcpy(name, num, sizeof(name));
			cp = name;
			pp = strchr(cp, ':');
			*pp = '\0';
			port_str = pp + 1;
		}
	} else {
		cp = num + 1;
		np = name; 
		while (*cp && ']' != *cp)
			*np++ = *cp++;
		*np = 0;
		if (']' == cp[0] && ':' == cp[1] && '\0' != cp[2])
			port_str = &cp[2];
		cp = name; 
	}
	ZERO(hints);
	hints.ai_flags = Z_AI_NUMERICHOST;
	err = getaddrinfo(cp, "ntp", &hints, &ai);
	if (err != 0)
		return 0;
	NTP_INSIST(ai->ai_addrlen <= sizeof(*netnum));
	ZERO(*netnum);
	memcpy(netnum, ai->ai_addr, ai->ai_addrlen);
	freeaddrinfo(ai);
	if (NULL == port_str || 1 != sscanf(port_str, "%hu", &port))
		port = NTP_PORT;
	SET_PORT(netnum, port);
	return 1;
}
Exemple #13
0
/*
 * Juergen Perlinger, 2008-11-12
 * Add support for full calendar calculatios. If the day-of-year is provided
 * (that is, not zero) it will be used instead of month and day-of-month;
 * otherwise a full turn through the calendar calculations will be taken.
 *
 * I know that Harlan Stenn likes to see assertions in production code, and I
 * agree there, but it would be a tricky thing here. The algorithm is quite
 * capable of producing sensible answers even to seemingly weird inputs: the
 * date <any year here>-03-00, the 0.th March of the year, will be automtically
 * treated as the last day of February, no matter whether the year is a leap
 * year or not. So adding constraints is merely for the benefit of the callers,
 * because the only thing we can check for consistency is our input, produced
 * by somebody else.
 *
 * BTW: A total roundtrip using 'caljulian' would be a quite shaky thing:
 * Because of the truncation of the NTP time stamp to 32 bits and the epoch
 * unfolding around the current time done by 'caljulian' the roundtrip does
 * *not* necessarily reproduce the input, especially if the time spec is more
 * than 68 years off from the current time...
 */
u_long
caltontp(
	const struct calendar *jt
	)
{
	ntp_u_int32_t days;	/* full days in NTP epoch */
	ntp_u_int32_t years;	/* complete ACE years before date */
	ntp_u_int32_t month;	/* adjusted month for calendar */
	
	NTP_INSIST(jt != NULL);

	NTP_REQUIRE(jt->month <= 13);	/* permit month 0..13! */
	NTP_REQUIRE(jt->monthday <= 32);
	NTP_REQUIRE(jt->yearday <= 366);
	NTP_REQUIRE(jt->hour <= 24);
	NTP_REQUIRE(jt->minute <= MINSPERHR);
	NTP_REQUIRE(jt->second <= SECSPERMIN);

	/*
	 * First convert the date to fully elapsed days since NTP epoch. The
	 * expressions used here give us initially days since 0001-01-01, the
	 * beginning of the christian era in the proleptic gregorian calendar;
	 * they are rebased on-the-fly into days since beginning of the NTP
	 * epoch, 1900-01-01.
	 */
	if (jt->yearday) {
		/*
		 * Assume that the day-of-year contains a useable value and
		 * avoid all calculations involving month and day-of-month.
		 */
		years = jt->year - 1;
		days  = years * DAYSPERYEAR	/* days in previous years */
		      + years / 4		/* plus prior years's leap days */
		      - years / 100		/* minus leapless century years */
		      + years / 400		/* plus leapful Gregorian yrs */
		      + jt->yearday		/* days this year */
		      - DAY_NTP_STARTS;		/* rebase to NTP epoch */
	} else {
		/*
		 * The following code is according to the excellent book
		 * 'Calendrical Calculations' by Nachum Dershowitz and Edward
		 * Reingold. It does a full calendar evaluation, using one of
		 * the alternate algorithms: Shift to a hypothetical year
		 * starting on the previous march,1st; merge years, month and
		 * days; undo the the 9 month shift (which is 306 days). The
		 * advantage is that we do NOT need to now whether a year is a
		 * leap year or not, because the leap day is the LAST day of
		 * the year.
		 */
		month  = (ntp_u_int32_t)jt->month + 9;
		years  = jt->year - 1 + month / 12;
		month %= 12;
		days   = years * DAYSPERYEAR	/* days in previous years */
		       + years / 4		/* plus prior years's leap days */
		       - years / 100		/* minus leapless century years */
		       + years / 400		/* plus leapful Gregorian yrs */
		       + (month * 153 + 2) / 5	/* plus days before month */
		       + jt->monthday		/* plus day-of-month */
		       - 306			/* minus 9 months */
		       - DAY_NTP_STARTS;	/* rebase to NTP epoch */
	}

	/*
	 * Do the obvious: Merge everything together, making sure integer
	 * promotion doesn't play dirty tricks on us; there is probably some
	 * redundancy in the casts, but this drives it home with force. All
	 * arithmetic is done modulo 2**32, because the result is truncated
	 * anyway.
	 */
	return               days       * SECSPERDAY
	    + (ntp_u_int32_t)jt->hour   * MINSPERHR*SECSPERMIN
	    + (ntp_u_int32_t)jt->minute * SECSPERMIN
	    + (ntp_u_int32_t)jt->second;
}