#include <sys/types.h>
#include <netinet/in.h>

#include "VXtime.h"
#include <stdio.h>
#include <ctype.h>
#include "VXnetdb.h"
#include "sockLib.h"
#include <errno.h>
#include "inetLib.h"
#include "snmp.h"
#include "asn1.h"
#include "snmp_impl.h"
#include "snmp_api.h"
#include "snmp_client.h"
#include "party.h"
#include "context.h"
#include "view.h"
#include "acl.h"
#include "net/if.h"
#include "in.h"

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long LONG;


/* here are some SNMP trap stuff */

int             snmp_out_traps = 0;
 
#define NUM_THOSTS 4            /* XXX */
struct snmp_thost {
        struct in_addr addr;
};

struct snmp_thost snmp_thosts[NUM_THOSTS];

extern u_long   get_myaddr();
extern void     shift_array();
extern oid      version_id[9];

extern int      snmp_sd;
extern int      snmp_init_flag;
extern long     long_return;
extern int      vxTicks;
extern char     version_descr[32];

extern struct ifnet *ifnet;

/* to be removed. */
/*
int      snmp_init_flag = 0;
int      snmp_sd = 0;
*/


int
snmp_build_trap(u_char * out_data, int *length, void *sysOid, int sysOidLen,
     unsigned int myAddr, int trapType, int specificType, unsigned int time,
 oid * varName, int varNameLen, u_char varType, int varLen, u_char * varVal)
{
	int             version = SNMP_VERSION_1;
	int             sidLen = strlen("public\n");
	int             dummyLen;
	u_char         *out_auth, *out_header, *out_pdu, *out_varHeader;
	u_char         *out_varlist, *out_end;
	int             auth_shift, pdu_shift, list_shift;

	out_auth = out_data;
	out_header = snmp_auth_build(out_data, length, (u_char *) "public",
				     &sidLen, &version, 90);
	if (out_header == NULL) {
		printf("auth build failed\n");
		return 0;
	}
	out_pdu = asn_build_header(out_header, length, (u_char) TRP_REQ_MSG, 90);
	if (out_pdu == NULL) {
		printf( "header build failed\n");
		return 0;
	}
	out_data = asn_build_objid(out_pdu, length,
		   (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
				   (oid *) sysOid, sysOidLen);
	if (out_data == NULL) {
		printf( "build enterprise failed\n");
		return 0;
	}
	out_data = asn_build_string(out_data, length,
				    (u_char) (IPADDRESS | ASN_PRIMITIVE),
				    (u_char *) & myAddr, sizeof(myAddr));
	if (out_data == NULL) {
		printf("build agent_addr failed\n");
		return 0;
	}
	out_data = asn_build_int(out_data, length,
		     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
				 (int *) &trapType, sizeof(trapType));
	if (out_data == NULL) {
		printf("build trap_type failed\n");
		return 0;
	}
	out_data = asn_build_int(out_data, length,
		     (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
			       (int *) &specificType, sizeof(specificType));
	if (out_data == NULL) {
		printf("build specificType failed\n");
		return 0;
	}
	out_varHeader = asn_build_unsigned_int(out_data, length,
				       (u_char) (TIMETICKS | ASN_PRIMITIVE),
					       &time, sizeof(time));
	if (out_varHeader == NULL) {
		printf("build timestampfailed\n");
		return 0;
	}
	out_varlist = asn_build_header(out_varHeader, length,
			     (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 90);
	out_end = snmp_build_var_op(out_varlist, varName, &varNameLen,
				    varType, varLen, varVal, length);
	if (out_end == NULL) {
		printf("build varop failed\n");
		return 0;
	}
	/*
         * Because of the assumption above that header lengths would be
         * encoded in one byte, things need to be fixed, now that the actual
         * lengths are known.
         */
	list_shift = 0;

	*length = out_end - out_varlist;
	if (*length >= 0x80) {
		list_shift++;
		if (*length > 0xFF)
			list_shift++;
	}
	pdu_shift = 0;
	*length = (out_end - out_pdu) + list_shift;
	if (*length >= 0x80) {
		pdu_shift++;
		if (*length > 0xFF)
			pdu_shift++;
	}
	auth_shift = 0;

	/* 2 below is the size of the assumed asn header in the auth header */
	*length = (out_end - out_auth) - 2 + pdu_shift + list_shift;
	if (*length >= 0x80) {
		auth_shift++;
		if (*length > 0xFF)
			auth_shift++;
	}
	if (auth_shift + pdu_shift + list_shift) {
		/*
	         * Shift packet (from start of varlist to end of packet) by
	         * the sum of the necessary shift counts.
	         */
		shift_array(out_varlist, out_end - out_varlist,
			    auth_shift + pdu_shift + list_shift);
		/* Now adjust pointers into the packet */
		out_end += auth_shift + pdu_shift + list_shift;
		out_varlist += auth_shift + pdu_shift + list_shift;
		out_varHeader += auth_shift + pdu_shift;
	}
	/* Now rebuild header with the actual lengths */
	dummyLen = out_end - out_varlist;
	if (asn_build_header(out_varHeader, &dummyLen,
			     (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
			     dummyLen) != out_varlist)
		return 0;

	if (auth_shift + pdu_shift) {
		/*
	         * Shift packet (from start of varlist to end of packet) by
	         * the sum of the necessary shift counts.
	         */
		shift_array(out_pdu, out_varHeader - out_pdu, auth_shift + pdu_shift);
		/* Now adjust pointers into the packet */
		out_pdu += auth_shift + pdu_shift;
		out_header += auth_shift;
	}
	/* Now rebuild header with the actual lengths */
	dummyLen = out_end - out_pdu;
	if (asn_build_header(out_header, &dummyLen,
			     (u_char) TRP_REQ_MSG, dummyLen) != out_pdu)
		return 0;

	out_data = out_auth;
	*length = out_end - out_auth;
	out_data = snmp_auth_build(out_data, length,
				   (u_char *) "public", &sidLen,
				   &version, out_end - out_header);
	if (out_data != out_header) {
		printf("internal error\n");
		return 0;
	}
	*length = out_end - out_auth;
	return *length;
}

void
snmp_trap_host(int trapType, int specificType, unsigned int srcAddr,
	       unsigned int destAddr, oid * var_name,
	       int var_name_len, u_char var_val_type,
	       int var_val_len, u_char * var_val)
{
	register u_char *out_data;
	int             out_length, len;
	struct sockaddr_in dest;
	static char    *buf[256];	/* XXX forwarded to sendto */
	int             ret;
	struct in_addr  inaddr;

	out_data = (u_char *) buf;
	out_length = 256;

	if (var_name == (oid *) 0) {
		/* no var values sent down */
		/*
		 * Use of ASN_NULL (instead of ASN_OCTET_STR)
		 * eliminates hex display of version_descr
		 * from SNMPc log display 
		 */
		len = snmp_build_trap(out_data, &out_length, version_id,
				      sizeof(version_id) / sizeof(oid),
				      srcAddr, trapType, specificType,
				      vxTicks, version_id,
				      sizeof(version_id) / sizeof(oid),
				      ASN_NULL,  /* ASN_OCTET_STR, */
				      strlen(version_descr),
				      (u_char *) version_descr);
	} else {
		len = snmp_build_trap(out_data, &out_length, version_id,
				      sizeof(version_id) / sizeof(oid),
				      srcAddr, trapType, specificType,
				      vxTicks, var_name, var_name_len,
				      var_val_type,
				      var_val_len, var_val);
	}

	dest.sin_addr.s_addr = destAddr;
	dest.sin_family = AF_INET;
	dest.sin_port = htons(SNMP_TRAP_PORT);

	inaddr.s_addr = destAddr;



	if ((ret = sendto(snmp_sd, (char *) out_data, out_length, 0,
			  (struct sockaddr *) & dest, sizeof(dest))) < 0) {
		printf("can't send trap %d to host %I", trapType, inaddr.s_addr);
		return;
	}


	/* printf("sent SNMP trap %d to host %x", trapType, inaddr.s_addr); */

	snmp_out_traps++;

}

void
snmp_link_trap(int trapType, int if_index)
{
	int             i;
	oid             ifid[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 1};
	unsigned int    myAddr;

	if (!snmp_init_flag)
		return;

	/*
	 * TODO: check to see if link trap sending is enabled
	 */

	myAddr = get_myaddr();
	ifid[10] = if_index;
	long_return = if_index;

	for (i = 0; i < NUM_THOSTS; i++) {
		if (snmp_thosts[i].addr.s_addr != INADDR_ANY)
			snmp_trap_host(trapType, 0, myAddr,
				       snmp_thosts[i].addr.s_addr,
			      ifid, sizeof(ifid) / sizeof(oid), ASN_INTEGER,
				sizeof(long_return), (char *) &long_return);
	}
}

void
snmp_trap(int trapType, int specificType)
{
	int             i;
	unsigned int    myAddr;

	if (!snmp_init_flag)
		return;

	myAddr = get_myaddr();

	for (i = 0; i < NUM_THOSTS; i++) {
		if (snmp_thosts[i].addr.s_addr != INADDR_ANY)
			snmp_trap_host(trapType, specificType, myAddr,
				 snmp_thosts[i].addr.s_addr, 0, 0, 0, 0, 0);
	}
}

void
snmp_send_link_trap(struct ifnet * ifp_in, int trap)
{
	int             i;
	struct ifnet   *ifp;

	for (i = 1, ifp = ifnet; ifp != 0; ifp = ifp->if_next, i++) {
		if (ifp == ifp_in)
			snmp_link_trap(trap, i);
	}
}

void
snmp_send_link_traps()
{
	struct ifnet   *ifp;
	int             i;

	for (i = 1, ifp = ifnet; ifp != 0; ifp = ifp->if_next, i++) {
		if (ifp->if_flags & IFF_UP)
			snmp_link_trap(SNMP_TRAP_LINKDOWN, i);
		else
			snmp_link_trap(SNMP_TRAP_LINKUP, i);	
	}
}

void
snmp_init_thosts()
{
	 bzero((char *)snmp_thosts, sizeof(snmp_thosts));
}

#ifdef VXWORKS

void
get_snmp_trap_ips (struct in_addr ipBuf[], int *ipCount)
{
        int i, count = 0;

	for (i = 0; i < NUM_THOSTS; i++) {
		if (snmp_thosts[i].addr.s_addr != INADDR_ANY)
		        ipBuf[count++].s_addr = snmp_thosts[i].addr.s_addr;  
	}
        *ipCount = count;
}


void
set_snmp_trap_ips (struct in_addr ipBuf[], int ipCount)
{
        int i;

        if (ipCount == 0)
	    return;
        snmp_init_thosts(); 
	for (i = 0; i < ipCount; i++)
		snmp_thosts[i].addr.s_addr = ipBuf[i].s_addr;
}

#endif

#define TEST
#ifdef TEST
#if 0
u_int adr[4] = {0xac191f11,0xac181f11,0xac201f11,0xac211f11};
#endif

void
snmp_trap_test(char *ipaddr)
{
        int i;

        snmp_init_thosts(); 
#if 0
	snmp_thosts[i].addr.s_addr = adr[i]; 
#endif
	for (i = 0; i < 4; i++){
		snmp_thosts[i].addr.s_addr = inet_addr(ipaddr);
		printf("SNMP trap host is ... %x\n",snmp_thosts[i].addr.s_addr);
	}
	snmp_send_link_traps();
}
#endif
