/* agent_trap.c: define trap generation routines for mib modules, etc,
   to use */

#include <config.h>

#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if TIME_WITH_SYS_TIME
# ifdef WIN32
#  include <sys/timeb.h>
# else
#  include <sys/time.h>
# endif
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#elif HAVE_WINSOCK_H
#include <winsock.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <getopt.h>

#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif

#include "asn1.h"
#include "snmp_api.h"
#include "snmp_impl.h"
#include "snmp_client.h"
#include "snmp.h"
#include "system.h"
#include "read_config.h"
#include "snmp_debug.h"
#include "snmp_parse_args.h"
#include "agent_trap.h"
#include "callback.h"
#include "agent_callbacks.h"
#include "tools.h"
#include "snmp_logging.h"
#include "callback.h"

#include "mib_module_config.h"

struct trap_sink {
    struct snmp_session	*sesp;
    struct trap_sink	*next;
    int			pdutype;
    int			version;
};

struct trap_sink *sinks	  = NULL;

extern struct timeval	starttime;

#define OID_LENGTH(x)  (sizeof(x)/sizeof(x[0]))

oid objid_enterprisetrap[] = { EXTENSIBLEMIB, 251 };
oid version_id[]	   = { EXTENSIBLEMIB, AGENTID, OSTYPE };
size_t enterprisetrap_len = OID_LENGTH( objid_enterprisetrap );
size_t version_id_len     = OID_LENGTH( version_id );

#define SNMPV2_TRAPS_PREFIX	SNMP_OID_SNMPMODULES,1,1,5
oid  cold_start_oid[] =		{ SNMPV2_TRAPS_PREFIX, 1 };	/* SNMPv2-MIB */
oid  warm_start_oid[] =		{ SNMPV2_TRAPS_PREFIX, 2 };	/* SNMPv2-MIB */
oid  link_down_oid[] =		{ SNMPV2_TRAPS_PREFIX, 3 };	/* IF-MIB */
oid  link_up_oid[] =		{ SNMPV2_TRAPS_PREFIX, 4 };	/* IF-MIB */
oid  auth_fail_oid[] =		{ SNMPV2_TRAPS_PREFIX, 5 };	/* SNMPv2-MIB */
oid  egp_xxx_oid[] =		{ SNMPV2_TRAPS_PREFIX, 99 };	/* ??? */

#define SNMPV2_TRAP_OBJS_PREFIX	SNMP_OID_SNMPMODULES,1,1,4
oid  snmptrap_oid[] 	      =	{ SNMPV2_TRAP_OBJS_PREFIX, 1, 0 };
oid  snmptrapenterprise_oid[] =	{ SNMPV2_TRAP_OBJS_PREFIX, 3, 0 };
oid  sysuptime_oid[] 	      =	{ SNMP_OID_MIB2,1,3,0 };
size_t  snmptrap_oid_len 	      =	OID_LENGTH(snmptrap_oid);
size_t  snmptrapenterprise_oid_len = OID_LENGTH(snmptrapenterprise_oid);
size_t  sysuptime_oid_len 	      =	OID_LENGTH(sysuptime_oid);


#define SNMP_AUTHENTICATED_TRAPS_ENABLED	1
#define SNMP_AUTHENTICATED_TRAPS_DISABLED	2

int	 snmp_enableauthentraps	= SNMP_AUTHENTICATED_TRAPS_DISABLED;
int	 snmp_enableauthentrapsset = 0;
char	*snmp_trapcommunity	= NULL;

/* Prototypes */
 /*
static int create_v1_trap_session (const char *, u_short, const char *);
static int create_v2_trap_session (const char *, u_short, const char *);
static int create_v2_inform_session (const char *, u_short, const char *);
static void free_trap_session (struct trap_sink *sp);
static void send_v1_trap (struct snmp_session *, int, int);
static void send_v2_trap (struct snmp_session *, int, int, int);
 */


	/*******************
	 *
	 * Trap session handling
	 *
	 *******************/

static void free_trap_session (struct trap_sink *sp)
{
    snmp_close(sp->sesp);
    free (sp);
}

int add_trap_session( struct snmp_session *ss, int pdutype, int confirm, int version )
{
    if (snmp_callback_available(SNMP_CALLBACK_APPLICATION,
                                SNMPD_CALLBACK_REGISTER_NOTIFICATIONS) ==
        SNMPERR_SUCCESS) {
        /* something else wants to handle notification registrations */
        struct agent_add_trap_args args;
        DEBUGMSGTL(("add_trap_session","adding callback trap sink\n"));
        args.ss = ss;
        args.confirm = confirm;
        snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
                       SNMPD_CALLBACK_REGISTER_NOTIFICATIONS,
                       (void *) &args);
    } else {
        /* no other support exists, handle it ourselves. */
        struct trap_sink *new_sink;
    
        DEBUGMSGTL(("add_trap_session","adding internal trap sink\n"));
        new_sink = (struct trap_sink *) malloc (sizeof (*new_sink));
        if ( new_sink == NULL )
            return 0;

        new_sink->sesp    = ss;
        new_sink->pdutype = pdutype;
        new_sink->version = version;
        new_sink->next    = sinks;
        sinks = new_sink;
    }
    return 1;
}

int remove_trap_session( struct snmp_session *ss ) {
    struct trap_sink *sp = sinks, *prev = 0;
    while (sp) {
        if (sp->sesp == ss) {
            if (prev) {
                prev->next = sp->next;
            } else {
                sinks = sp->next;
            }
            free_trap_session(sp);
            return 1;
        }
        prev = sp;
	sp = sp->next;
    }
    return 0;
}

int create_trap_session (char *sink, u_short sinkport,
				char *com,
				int version, int pdutype)
{
    struct snmp_session	 session, *sesp;

    memset (&session, 0, sizeof (struct snmp_session));
    session.peername = sink;
    session.version = version;
    if (com) {
        session.community = (u_char *)com;
        session.community_len = strlen (com);
    }
    session.remote_port = sinkport;
    sesp = snmp_open (&session);

    if (sesp) {
	return( add_trap_session( sesp, pdutype, (pdutype==SNMP_MSG_INFORM), version ));
    }

    /* diagnose snmp_open errors with the input struct snmp_session pointer */
    snmp_sess_perror("snmpd: create_trap_session", &session);
    return 0;
}

static int create_v1_trap_session (char *sink, u_short sinkport,
				   char *com)
{
    return create_trap_session( sink, sinkport, com,
				SNMP_VERSION_1, SNMP_MSG_TRAP );
}

static int create_v2_trap_session (char *sink,  u_short sinkport,
				   char *com)
{
    return create_trap_session( sink, sinkport, com,
				SNMP_VERSION_2c, SNMP_MSG_TRAP2 );
}

static int create_v2_inform_session (char *sink,  u_short sinkport,
				     char *com)
{
    return create_trap_session( sink, sinkport, com,
				SNMP_VERSION_2c, SNMP_MSG_INFORM );
}


void snmpd_free_trapsinks (void)
{
    struct trap_sink *sp = sinks;
    while (sp) {
	sinks = sinks->next;
	free_trap_session(sp);
	sp = sinks;
    }
}

	/*******************
	 *
	 * Trap handling
	 *
	 *******************/

void convert_v2_to_v1( struct variable_list *vars, struct snmp_pdu *template_pdu )
{
    struct variable_list *v, *trap_v=NULL, *ent_v=NULL;
    oid  trap_prefix[] = { SNMPV2_TRAPS_PREFIX };
    int len;

    for ( v = vars ; v ; v = v->next_variable ) {
	if ( snmp_oid_compare( v->name, v->name_length, 
    			       snmptrap_oid, OID_LENGTH(snmptrap_oid)) == 0 )
		trap_v = v;
	if ( snmp_oid_compare( v->name, v->name_length, 
    			       snmptrapenterprise_oid,
			       OID_LENGTH(snmptrapenterprise_oid)) == 0 )
		ent_v = v;
    }

    if ( !trap_v )
	return;		/* Can't find v2 snmpTrapOID varbind */

		/*
		 * Is this a 'standard' trap?
		 *  Or at least, does it have the correct prefix?
		 */
    if ( snmp_oid_compare( trap_v->val.objid, OID_LENGTH(trap_prefix), 
    			   trap_prefix,       OID_LENGTH(trap_prefix)) == 0 ) {
	template_pdu->trap_type     = trap_v->val.objid[OID_LENGTH(trap_prefix)]-1;
	template_pdu->specific_type = 0;
    }
    else {
	len = trap_v->val_len /sizeof( oid );
	template_pdu->trap_type     = 6;	/* enterprise specific */
	template_pdu->specific_type = trap_v->val.objid[len-1];
    }

		/*
		 *  TODO:
		 *    Extract the appropriate enterprise value from 'ent_v'
		 *    Remove uptime/trapOID varbinds from 'vars' list
		 */
    
}

void send_enterprise_trap_vars (int trap, 
		     int specific,
		     oid *enterprise, int enterprise_length,
		     struct variable_list *vars)
{
    struct variable_list uptime_var, snmptrap_var, enterprise_var;
    struct variable_list *v2_vars, *last_var=NULL;
    struct snmp_pdu	*template_pdu;
    struct timeval	 now;
    long uptime;
    struct sockaddr_in *pduIp;
    struct trap_sink *sink;
    oid temp_oid[MAX_OID_LEN];
    
		/*
		 * Initialise SNMPv2 required variables
		 */
    gettimeofday(&now, NULL);
    uptime = calculate_time_diff(&now, &starttime);
    memset (&uptime_var, 0, sizeof (struct variable_list));
    snmp_set_var_objid( &uptime_var, sysuptime_oid, OID_LENGTH(sysuptime_oid));
    snmp_set_var_value( &uptime_var, (u_char *)&uptime, sizeof(uptime) );
    uptime_var.type           = ASN_TIMETICKS;
    uptime_var.next_variable  = &snmptrap_var;

    memset (&snmptrap_var, 0, sizeof (struct variable_list));
    snmp_set_var_objid( &snmptrap_var, snmptrap_oid, OID_LENGTH(snmptrap_oid));
	/* value set later .... */
    snmptrap_var.type           = ASN_OBJECT_ID;
    if ( vars )
	snmptrap_var.next_variable  = vars;
    else
	snmptrap_var.next_variable  = &enterprise_var;

			/* find end of provided varbind list,
			   ready to append the enterprise info if necessary */
    last_var = vars;
    while ( last_var && last_var->next_variable )
	last_var = last_var->next_variable;

    memset (&enterprise_var, 0, sizeof (struct variable_list));
    snmp_set_var_objid( &enterprise_var,
		 snmptrapenterprise_oid, OID_LENGTH(snmptrapenterprise_oid));
    snmp_set_var_value( &enterprise_var, (u_char *)enterprise, enterprise_length*sizeof(oid));
    enterprise_var.type           = ASN_OBJECT_ID;
    enterprise_var.next_variable  = NULL;

    v2_vars = &uptime_var;

		/*
		 *  Create a template PDU, ready for sending
		 */
    template_pdu = snmp_pdu_create( SNMP_MSG_TRAP );
    if ( template_pdu == NULL ) {
		/* Free memory if value stored dynamically */
	snmp_set_var_value( &enterprise_var, NULL, 0);
	return;
    }
    template_pdu->trap_type     = trap;
    template_pdu->specific_type = specific;
    if ( snmp_clone_mem((void **)&template_pdu->enterprise,
				enterprise, enterprise_length*sizeof(oid))) {
	snmp_free_pdu( template_pdu );
	snmp_set_var_value( &enterprise_var, NULL, 0);
	return;
    }
    template_pdu->enterprise_length = enterprise_length;
    template_pdu->flags |= UCD_MSG_FLAG_FORCE_PDU_COPY;
    pduIp = (struct sockaddr_in *)&template_pdu->agent_addr;
    pduIp->sin_family		 = AF_INET;
    pduIp->sin_addr.s_addr	 = get_myaddr();
    template_pdu->time		 	 = uptime;

		/*
		 *  Now use the parameters to determine
		 *    which v2 variables are needed,
		 *    and what values they should take.
		 */
    switch ( trap ) {
	case -1:	/*
			 *	SNMPv2 only
			 *  Check to see whether the variables provided
			 *    are sufficient for SNMPv2 notifications
			 */
		if (vars && snmp_oid_compare(vars->name, vars->name_length,
				sysuptime_oid, OID_LENGTH(sysuptime_oid)) == 0 )
			v2_vars = vars;
		else
		if (vars && snmp_oid_compare(vars->name, vars->name_length,
				snmptrap_oid, OID_LENGTH(snmptrap_oid)) == 0 )
			uptime_var.next_variable = vars;
		else {
			/* Hmmm... we don't seem to have a value - oops! */
			snmptrap_var.next_variable = vars;
		}
		last_var = NULL;	/* Don't need enterprise info */
		convert_v2_to_v1( vars, template_pdu );
		break;

			/* "Standard" SNMPv1 traps */

	case SNMP_TRAP_COLDSTART:
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)cold_start_oid,
				    sizeof(cold_start_oid));
		break;
	case SNMP_TRAP_WARMSTART:
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)warm_start_oid,
				    sizeof(warm_start_oid));
		break;
	case SNMP_TRAP_LINKDOWN:
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)link_down_oid,
				    sizeof(link_down_oid));
		break;
	case SNMP_TRAP_LINKUP:
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)link_up_oid,
				    sizeof(link_up_oid));
		break;
	case SNMP_TRAP_AUTHFAIL:
		if (snmp_enableauthentraps == SNMP_AUTHENTICATED_TRAPS_DISABLED) {
		    snmp_free_pdu(template_pdu);
		    snmp_set_var_value( &enterprise_var, NULL, 0);
		    return;
		}
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)auth_fail_oid,
				    sizeof(auth_fail_oid));
		break;
	case SNMP_TRAP_EGPNEIGHBORLOSS:
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)egp_xxx_oid,
				    sizeof(egp_xxx_oid));
		break;

	case SNMP_TRAP_ENTERPRISESPECIFIC:
		memcpy( temp_oid,
				    (char *)enterprise,
				    (enterprise_length)*sizeof(oid));
		temp_oid[ enterprise_length   ] = 0;
		temp_oid[ enterprise_length+1 ] = specific;
		snmp_set_var_value( &snmptrap_var,
				    (u_char *)temp_oid,
				    (enterprise_length+2)*sizeof(oid));
		snmptrap_var.next_variable  = vars;
		last_var = NULL;	/* Don't need version info */
		break;
    }
    

		/*
		 *  Now loop through the list of trap sinks,
		 *   sending an appropriately formatted PDU to each
		 */
    for ( sink = sinks ; sink ; sink=sink->next ) {
	if ( sink->version == SNMP_VERSION_1 && trap == -1 )
		continue;	/* Skip v1 sinks for v2 only traps */
	template_pdu->command = sink->pdutype;

	if ( sink->version != SNMP_VERSION_1 ) {
	    template_pdu->variables = v2_vars;
	    if ( last_var )
		last_var->next_variable = &enterprise_var;
	}
	else
	    template_pdu->variables = vars;

        send_trap_to_sess(sink->sesp, template_pdu);

	if ( sink->version != SNMP_VERSION_1 && last_var )
	    last_var->next_variable = NULL;
    }

    /* send stuff to registered callbacks */
    /* v2 traps/informs */
    template_pdu->variables = v2_vars;
    if ( last_var )
        last_var->next_variable = &enterprise_var;

    snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, SNMPD_CALLBACK_SEND_TRAP2,
                        template_pdu);
    
    if ( last_var )
        last_var->next_variable = NULL;

    /* v1 traps */
    template_pdu->command = SNMP_MSG_TRAP;
    template_pdu->variables = vars;
    
    snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, SNMPD_CALLBACK_SEND_TRAP1,
                        template_pdu);

		/* Free memory if values stored dynamically */
    snmp_set_var_value( &enterprise_var, NULL, 0);
    snmp_set_var_value( &snmptrap_var, NULL, 0);
	/* Ensure we don't free anything we shouldn't */
    if ( last_var )
	last_var->next_variable = NULL;
    template_pdu->variables = NULL;
    snmp_free_pdu( template_pdu );
}

/* send_trap_to_sess: sends a trap to a session but assumes that the
   pdu is constructed correctly for the session type. */
void send_trap_to_sess(struct snmp_session *sess,
                       struct snmp_pdu *template_pdu) {
    struct snmp_pdu *pdu;

    if (!sess || !template_pdu)
        return;

    DEBUGMSGTL(("send_trap_to_sess","sending trap type=%d, version=%d\n",
                template_pdu->command, sess->version));
    
    if (sess->version == SNMP_VERSION_1 &&
        (template_pdu->command == SNMP_MSG_TRAP2 ||
         template_pdu->command == SNMP_MSG_INFORM))
        return;	/* Skip v1 sinks for v2 only traps */
    template_pdu->version = sess->version;
    pdu = snmp_clone_pdu( template_pdu );
    pdu->sessid = sess->sessid;	/* AgentX only ? */
    if ( snmp_send( sess, pdu) == 0 ) {
        snmp_sess_perror ("snmpd: send_trap", sess);
        snmp_free_pdu( pdu );
    }
    else {
        snmp_increment_statistic(STAT_SNMPOUTTRAPS);
        snmp_increment_statistic(STAT_SNMPOUTPKTS);
    }
}

void send_trap_vars (int trap, 
		     int specific,
		     struct variable_list *vars)
{
    if ( trap == SNMP_TRAP_ENTERPRISESPECIFIC )
        send_enterprise_trap_vars( trap, specific, objid_enterprisetrap,
			OID_LENGTH(objid_enterprisetrap), vars );
    else
        send_enterprise_trap_vars( trap, specific, version_id,
			OID_LENGTH(version_id), vars );
}

void send_easy_trap (int trap, 
		     int specific)
{
    send_trap_vars( trap, specific, NULL );
}

void send_v2trap ( struct variable_list *vars)
{
    send_trap_vars( -1, -1, vars );
}

void
send_trap_pdu(struct snmp_pdu *pdu)
{
    send_trap_vars( -1, -1, pdu->variables );
}



	/*******************
	 *
	 * Config file handling
	 *
	 *******************/

void snmpd_parse_config_authtrap(const char *token, 
				 char *cptr)
{
    int i;

    i = atoi(cptr);
    if (i == 0) {
	if (strcmp(cptr, "enable") == 0) {
	    i = SNMP_AUTHENTICATED_TRAPS_ENABLED;
	} else if (strcmp(cptr, "disable") == 0) {
	    i = SNMP_AUTHENTICATED_TRAPS_DISABLED;
	}
    }
    if (i < 1 || i > 2) {
	config_perror("authtrapenable must be 1 or 2");
    } else {
	if (strcmp(token, "pauthtrapenable") == 0) {
	    if (snmp_enableauthentrapsset < 0) {
		/*  This is bogus (and shouldn't happen anyway) -- the value
		    of snmpEnableAuthenTraps.0 is already configured
		    read-only.  */
		snmp_log(LOG_WARNING, "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n");
		return;
	    } else {
		snmp_enableauthentrapsset++;
	    }
	} else {
	    if (snmp_enableauthentrapsset > 0) {
		/*  This is bogus (and shouldn't happen anyway) -- we already
		    read a persistent value of snmpEnableAuthenTraps.0, which
		    we should ignore in favour of this one.  */
		snmp_log(LOG_WARNING, "ignoring attempted override of read-only snmpEnableAuthenTraps.0\n");
		/*  Fall through and copy in this value.  */
	    }
	    snmp_enableauthentrapsset = -1;
	}
	snmp_enableauthentraps = i;
    }
}

void snmpd_parse_config_trapsink(const char *token, 
				 char *cptr)
{
    char tmpbuf[1024];
    char *sp, *cp, *pp = NULL;
    u_short sinkport;
    
    if (!snmp_trapcommunity) snmp_trapcommunity = strdup("public");
    sp = strtok(cptr, " \t\n");
    cp = strtok(NULL, " \t\n");
    if (cp) pp = strtok(NULL, " \t\n");
    if (cp && pp) {
	sinkport = atoi(pp);
	if ((sinkport < 1) || (sinkport > 0xffff)) {
	    config_perror("trapsink port out of range");
	    sinkport = SNMP_TRAP_PORT;
	}
    } else {
	sinkport = SNMP_TRAP_PORT;
    }
    if (create_v1_trap_session(sp, sinkport,
			       cp ? cp : snmp_trapcommunity) == 0) {
	sprintf(tmpbuf,"cannot create trapsink: %s", cptr);
	config_perror(tmpbuf);
    }
}


void
snmpd_parse_config_trap2sink(const char *word, char *cptr)
{
    char tmpbuf[1024];
    char *sp, *cp, *pp = NULL;
    u_short sinkport;
  
    if (!snmp_trapcommunity) snmp_trapcommunity = strdup("public");
    sp = strtok(cptr, " \t\n");
    cp = strtok(NULL, " \t\n");
    if (cp) pp = strtok(NULL, " \t\n");
    if (cp && pp) {
	sinkport = atoi(pp);
	if ((sinkport < 1) || (sinkport > 0xffff)) {
	    config_perror("trapsink port out of range");
	    sinkport = SNMP_TRAP_PORT;
	}
    } else {
	sinkport = SNMP_TRAP_PORT;
    }
    if (create_v2_trap_session(sp, sinkport,
			       cp ? cp : snmp_trapcommunity) == 0) {
	sprintf(tmpbuf,"cannot create trap2sink: %s", cptr);
	config_perror(tmpbuf);
    }
}

void
snmpd_parse_config_informsink(const char *word, char *cptr)
{
    char tmpbuf[1024];
    char *sp, *cp, *pp = NULL;
    u_short sinkport;
  
    if (!snmp_trapcommunity) snmp_trapcommunity = strdup("public");
    sp = strtok(cptr, " \t\n");
    cp = strtok(NULL, " \t\n");
    if (cp) pp = strtok(NULL, " \t\n");
    if (cp && pp) {
	sinkport = atoi(pp);
	if ((sinkport < 1) || (sinkport > 0xffff)) {
	    config_perror("trapsink port out of range");
	    sinkport = SNMP_TRAP_PORT;
	}
    } else {
	sinkport = SNMP_TRAP_PORT;
    }
    if (create_v2_inform_session(sp, sinkport,
				 cp ? cp : snmp_trapcommunity) == 0) {
	sprintf(tmpbuf,"cannot create informsink: %s", cptr);
	config_perror(tmpbuf);
    }
}

/* this must be standardized somewhere, right? */
#define MAX_ARGS 128

static int traptype;

static void trapOptProc(int argc, char *const *argv, int opt)
{
    switch (opt) {
        case 'C':
            while (*optarg) {
                switch (*optarg++) {
                    case 'i':
                        traptype = SNMP_MSG_INFORM;
                        break;
                    default:
                        config_perror("unknown argument passed to -C");
                        break;
                }
            }
            break;
    }
}


void
snmpd_parse_config_trapsess(const char *word, char *cptr) {
    char *argv[MAX_ARGS];
    int argn, arg;
    struct snmp_session session, *ss;

    /* inform or trap?  default to trap */
    traptype = SNMP_MSG_TRAP2;

    /* create the argv[] like array */
    argv[0] = strdup("snmpd-trapsess"); /* bogus entry for getopt() */
    for(argn = 1; cptr && argn < MAX_ARGS; argn++) {
	argv[argn] = strdup(cptr); /* more than enough room */
        cptr = copy_word(cptr, argv[argn]);
    }

    arg = snmp_parse_args(argn, argv, &session, "C:", trapOptProc);
#ifdef XXX_BROKEN /* parse_args doesn't clone memory like it should */
    do { free(argv[--argn]); } while (argn > 0);
#endif

    if (session.remote_port == SNMP_DEFAULT_REMPORT)
        session.remote_port = SNMP_TRAP_PORT;
    ss = snmp_open (&session);

    if (!ss) {
        config_perror("snmpd: failed to parse this line or the remote trap receiver is down.  Pausible cause:");
        snmp_sess_perror("snmpd: snmpd_parse_config_trapsess()", &session);
        return;
    }
    
    if (ss->version == SNMP_VERSION_1) {
	add_trap_session(ss, SNMP_MSG_TRAP, 0, SNMP_VERSION_1);
    } else {
	add_trap_session( ss, traptype, (traptype==SNMP_MSG_INFORM), ss->version );
    }
}

void
snmpd_parse_config_trapcommunity(const char *word, char *cptr)
{
    if (snmp_trapcommunity) free(snmp_trapcommunity);
    snmp_trapcommunity = (char *)malloc (strlen(cptr)+1);
    copy_word(cptr, snmp_trapcommunity);
}

void snmpd_free_trapcommunity (void)
{
    if (snmp_trapcommunity) {
	free(snmp_trapcommunity);
	snmp_trapcommunity = NULL;
    }
}
