/*
** libsnmptrap
** $Id: snmptrap.c,v 1.1 2008/02/04 04:22:29 sella Exp $
** Copyright (c) 2005,2006 James M. Sella. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

static const char rcsid[] = "$Id: snmptrap.c,v 1.1 2008/02/04 04:22:29 sella Exp $";

#include "snmptrap.h"
#include <errno.h>
#include <assert.h>

#ifdef HAVE_NET_SNMP

const oid objid_enterprise[] = { 1, 3, 6, 1, 4, 1, 3, 1, 1 };
const oid objid_sysdescr[] = { 1, 3, 6, 1, 2, 1, 1, 1, 0 };
const oid objid_sysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
const oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };

int send_snmptrap(snmptrap_t *trap) {
	snmptrap_peer_t *peer = NULL;
	snmptrap_varbind_t *varbind = NULL;
	netsnmp_session session, *ss = NULL;
	netsnmp_pdu *pdu = NULL, *response = NULL;
	in_addr_t *ip = NULL;
	oid name[MAX_OID_LEN];
	size_t name_length;
	char buf[1024];
	char public[] = "public";
	int ret_val = 0;

	assert(trap != NULL);

	peer = trap->peer;
	while (peer != NULL) {
		assert(peer->peername != NULL);
		assert(peer->version == SNMP_VERSION_1 || peer->version == SNMP_VERSION_2c || peer->version == SNMP_VERSION_3);

		snmp_sess_init(&session);

		session.peername = (unsigned char*) peer->peername;

		if (peer->community[0] == '\0') {
			session.community = (unsigned char*) public;
		} else {
			session.community = (unsigned char*) peer->community;
		}
		session.community_len = strlen(session.community);

		session.version = peer->version;

		session.callback = _callback;
		session.callback_magic = NULL;

		init_snmp(PROGRAM_NAME);

		SOCK_STARTUP;

		netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT, SNMP_TRAP_PORT);

		if ((ss = snmp_open(&session)) == NULL) {
			snmp_sess_perror("snmp_open", &session);
			ret_val = 1;

			goto error;
		}

		/* Perform version specific setup of trap */
		if (peer->version == SNMP_VERSION_1) {
			pdu = snmp_pdu_create(SNMP_MSG_TRAP);
			ip = (in_addr_t*) pdu->agent_addr;

			/* Set enterprise (Object ID) */
			if (trap->sysObjectID[0] == '\0') {
				/* Use: enterprises.dgt.dgtProducts.25439.dgsProducts.snmptrapMIB (.1.3.6.1.4.1.25439.1.3) */
				pdu->enterprise = (oid*) malloc(sizeof(objid_enterprise));
				memcpy(pdu->enterprise, objid_enterprise, sizeof(objid_enterprise));
				pdu->enterprise_length = sizeof(objid_enterprise) / sizeof(oid);
			} else {
				name_length = MAX_OID_LEN;
				if (!snmp_parse_oid(trap->sysObjectID, name, &name_length)) {
					snmp_perror(trap->sysObjectID);
					ret_val = 1;

					goto error;
				}
				pdu->enterprise = (oid*) malloc(name_length * sizeof(oid));
				memcpy(pdu->enterprise, name, name_length * sizeof(oid));
				pdu->enterprise_length = name_length;
			}

			/* Set agent */
			if (peer->agent[0] == '\0') {
				*ip = get_myaddr();
			} else {
				inet_ntop(AF_INET, ip, peer->agent, sizeof(peer->agent));
			}

			/* Set trap type and specific */
			pdu->trap_type = trap->trap_type;
			pdu->specific_type = trap->specific_type;

			/* Set description (sysuptime). */
			if (trap->sysUpTime < 1) {
				pdu->time = get_uptime();
			} else {
				pdu->time = trap->sysUpTime;
			}
		} else if (peer->version == SNMP_VERSION_2c) {
			pdu = snmp_pdu_create(peer->inform ? SNMP_MSG_INFORM : SNMP_MSG_TRAP2);

			/* Add sysuptime */
			if (trap->sysUpTime < 1) {
				snprintf(buf, sizeof(buf), "%ld", get_uptime()); /* Use server uptime */
			} else {
				snprintf(buf, sizeof(buf), "%ld", trap->sysUpTime);
			}
			snmp_add_var(pdu, objid_sysuptime, sizeof(objid_sysuptime) / sizeof(oid), 't', buf);

			/* Add Object ID */
			if (trap->sysObjectID[0] == '\0') {
				/* Use: enterprises.dgt.dgtProducts.25439.dgsProducts.libsnmptrapMIB (.1.3.6.1.4.1.25439.1.3) */
				snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', ".1.3.6.1.4.1.25439.1.3");
			} else {
				snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', trap->sysObjectID);
			}
		} else if (peer->version == SNMP_VERSION_3) {
			ret_val = 1;

			goto error;
		} else {
			ret_val = 1;

			goto error;
		}

		/* Insert variable bindings */
		varbind = trap->varbind;
		while (varbind != NULL) {
			name_length = MAX_OID_LEN;

			if (!snmp_parse_oid(varbind->var, name, &name_length)) {
				snmp_perror(varbind->var);
				ret_val = 1;

				goto error;
			}

			if (snmp_add_var(pdu, name, name_length, varbind->type, varbind->val) != 0) {
				snmp_perror(varbind->var);
				ret_val = 1;

				goto error;
			}

			varbind = varbind->next;
		}

		/* Transmit */
		if (peer->inform && peer->version != SNMP_VERSION_1) { /* Inform */
			if (snmp_synch_response(ss, pdu, &response)) {
				snmp_sess_perror("snmp_synch_response (inform)", ss);

				ret_val = 1;
			} else {
				snmp_free_pdu(response);
			}
		} else { /* Trap */
			if (snmp_send(ss, pdu) == 0) {
				snmp_sess_perror("snmp_send", ss);
				snmp_free_pdu(pdu);

				ret_val = 1;
			}
		}

error:
		if (ss) {
			snmp_close(ss);
			ss = NULL;
		}

		SOCK_CLEANUP;

		/* Move to next peer */
		peer = peer->next;
	}

	return ret_val;
}

int _callback(int operation, netsnmp_session *session, int reqid, netsnmp_pdu *pdu, void *magic) {
    return 1;
}

#else /* HAVE_NET_SNMP */

int send_snmptrap(snmptrap_t *trap) {
	assert(trap != NULL);

	return 0;
}

#endif /* HAVE_NET_SNMP */

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim: noet ts=3 sw=3
*/
