// $Id: NTP.cc,v 1.10 2002/10/26 17:03:43 vern Exp $
//
// Copyright (c) 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include "NetVar.h"
#include "NTP.h"
#include "Sessions.h"
#include "Event.h"


NTP_Session::NTP_Session(NetSessions* s, HashKey* k, double t, const ConnID* id,
		const struct udphdr* up)
: UDP_Connection(s, k, t, id, up)
	{
	ADD_TIMER(&NTP_Session::ExpireTimer, t + ntp_session_timeout, 1);
	}

void NTP_Session::Done()
	{
	Event(udp_session_done);
	UDP_Connection::Done();
	}

int NTP_Session::Request(double /* t */, const u_char* data, int len)
	{
	request_len += len;
	Message(data, len);
	return 1;
	}

int NTP_Session::Reply(double /* t */, const u_char* data, int len)
	{
	reply_len += len;
	Message(data, len);
	return 1;
	}

void NTP_Session::Message(const u_char* data, int len)
	{
	if ( (unsigned) len < sizeof(struct ntpdata) )
		{
		Weird("truncated_NTP");
		return;
		}

	struct ntpdata* ntp_data = (struct ntpdata *) data;
	len -= sizeof *ntp_data;
	data += sizeof *ntp_data;

	RecordVal* msg = new RecordVal(ntp_msg);

	unsigned int code = ntp_data->status & 0x7;

	msg->Assign(0, new Val((unsigned int) (ntohl(ntp_data->refid)), TYPE_COUNT));
	msg->Assign(1, new Val(code, TYPE_COUNT));
	msg->Assign(2, new Val((unsigned int) ntp_data->stratum, TYPE_COUNT));
	msg->Assign(3, new Val((unsigned int) ntp_data->ppoll, TYPE_COUNT));
	msg->Assign(4, new Val((unsigned int) ntp_data->precision, TYPE_INT));
	msg->Assign(5, new Val(ShortFloat(ntp_data->distance), TYPE_INTERVAL));
	msg->Assign(6, new Val(ShortFloat(ntp_data->dispersion), TYPE_INTERVAL));
	msg->Assign(7, new Val(LongFloat(ntp_data->reftime), TYPE_TIME));
	msg->Assign(8, new Val(LongFloat(ntp_data->org), TYPE_TIME));
	msg->Assign(9, new Val(LongFloat(ntp_data->rec), TYPE_TIME));
	msg->Assign(10, new Val(LongFloat(ntp_data->xmt), TYPE_TIME));

	val_list* vl = new val_list;
	vl->append(BuildConnVal());
	vl->append(msg);
	vl->append(new StringVal(new BroString(data, len, 0)));

	ConnectionEvent(ntp_message, vl);
	}

double NTP_Session::ShortFloat(struct s_fixedpt fp)
	{
	return ConvertToDouble(ntohs(fp.int_part), ntohs(fp.fraction), 65536.0);
	}

double NTP_Session::LongFloat(struct l_fixedpt fp)
	{
	double t = ConvertToDouble(ntohl(fp.int_part), ntohl(fp.fraction),
				   4294967296.0);

	return t ? t - JAN_1970 : 0.0;
	}

double NTP_Session::ConvertToDouble(unsigned int int_part,
				    unsigned int fraction, double frac_base)
	{
	return double(int_part) + double(fraction) / frac_base;
	}

void NTP_Session::ExpireTimer(double /* t */)
	{
	Event(connection_timeout);
	sessions->Remove(this, 1);
	}
