/* omsnmp.c
 *
 * This module sends an snmp trap.
 *
 * Copyright 2007-2018 Adiscon GmbH.
 *
 * This file is part of rsyslog.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *       -or-
 *       see COPYING.ASL20 in the source distribution
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "config.h"
#include "rsyslog.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>
#include <assert.h>
#include "conf.h"
#include "syslogd-types.h"
#include "cfsysline.h"
#include "module-template.h"

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "omsnmp.h"
#include "errmsg.h"
#include "parserif.h"

MODULE_TYPE_OUTPUT;
MODULE_TYPE_NOKEEP;
MODULE_CNFNAME("omsnmp")

/* internal structures
 */
DEF_OMOD_STATIC_DATA;

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

/* 4 */
#define SNMP_SOURCETEMPLATE "\"%fromhost-ip%\""


typedef struct _instanceData {
    uchar *szTransport; /* Transport - Can be udp, tcp, udp6, tcp6 and other types
               supported by NET-SNMP */
    uchar *szTarget; /* IP/hostname of Snmp Target*/
    uchar *szCommunity; /* Snmp Community */
    uchar *szEnterpriseOID; /* Snmp Enterprise OID-default is (1.3.6.1.4.1.3.1.1 = enterprises.cmu.1.1) */
    uchar *szSnmpTrapOID; /* Snmp Trap OID - default is (1.3.6.1.4.1.19406.1.2.1 =
                 ADISCON-MONITORWARE-MIB::syslogtrap) */
    uchar *szSyslogMessageOID; /* Snmp OID used for the Syslog Message:
                                * default is 1.3.6.1.4.1.19406.1.1.2.1 - ADISCON-MONITORWARE-MIB::syslogMsg
                                * You will need the ADISCON-MONITORWARE-MIB and ADISCON-MIB mibs installed on the
                                *receiver side in order to decode this mib. Downloads of these mib files can be found
                                *here: http://www.adiscon.org/download/ADISCON-MONITORWARE-MIB.txt
                                *	http://www.adiscon.org/download/ADISCON-MIB.txt
                                */
    uchar *szSnmpV1Source; /* If PDU source property needs to be overwritten, only valid for SNMPv1*/

    int iPort; /* Target Port */
    int iSNMPVersion; /* SNMP Version to use */
    int iTrapType; /* Snmp TrapType or GenericType */
    int iSpecificType; /* Snmp Specific Type */

    uchar *tplName; /* format template to use */
} instanceData;

typedef struct wrkrInstanceData {
    instanceData *pData;
    netsnmp_session *snmpsession; /* Holds to SNMP Session, NULL if not initialized */
} wrkrInstanceData_t;

typedef struct configSettings_s {
    uchar *pszTransport; /* default transport */
    uchar *pszTarget;
    /* note using an unsigned for a port number is not a good idea from an IPv6 point of view */
    int iPort;
    int iSNMPVersion; /* 0 Means SNMPv1, 1 Means SNMPv2c */
    uchar *pszCommunity;
    uchar *pszEnterpriseOID;
    uchar *pszSnmpTrapOID;
    uchar *pszSyslogMessageOID;

    int iSpecificType;
    int iTrapType; /*Default is SNMP_TRAP_ENTERPRISESPECIFIC */
    /*
                Possible Values
        SNMP_TRAP_COLDSTART		(0)
        SNMP_TRAP_WARMSTART		(1)
        SNMP_TRAP_LINKDOWN		(2)
        SNMP_TRAP_LINKUP		(3)
        SNMP_TRAP_AUTHFAIL		(4)
        SNMP_TRAP_EGPNEIGHBORLOSS	(5)
        SNMP_TRAP_ENTERPRISESPECIFIC	(6)
    */
} configSettings_t;
static configSettings_t cs;

/* tables for interfacing with the v6 config system */
/* action (instance) parameters */
static struct cnfparamdescr actpdescr[] = {{"server", eCmdHdlrString, CNFPARAM_REQUIRED},
                                           {"port", eCmdHdlrInt, 0},
                                           {"transport", eCmdHdlrString, 0},
                                           {"version", eCmdHdlrInt, 0},
                                           {"community", eCmdHdlrString, 0},
                                           {"enterpriseoid", eCmdHdlrString, 0},
                                           {"trapoid", eCmdHdlrString, 0},
                                           {"messageoid", eCmdHdlrString, 0},
                                           {"snmpv1dynsource", eCmdHdlrString, 0},
                                           {"traptype", eCmdHdlrInt, 0},
                                           {"specifictype", eCmdHdlrInt, 0},
                                           {"template", eCmdHdlrGetWord, 0}};
static struct cnfparamblk actpblk = {CNFPARAMBLK_VERSION, sizeof(actpdescr) / sizeof(struct cnfparamdescr), actpdescr};

BEGINinitConfVars /* (re)set config variables to default values */
    CODESTARTinitConfVars;
    cs.pszTransport = NULL;
    cs.pszTarget = NULL;
    cs.iPort = 0;
    cs.iSNMPVersion = 1;
    cs.pszCommunity = NULL;
    cs.pszEnterpriseOID = NULL;
    cs.pszSnmpTrapOID = NULL;
    cs.pszSyslogMessageOID = NULL;
    cs.iSpecificType = 0;
    cs.iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
ENDinitConfVars

BEGINcreateInstance
    CODESTARTcreateInstance;
ENDcreateInstance

BEGINcreateWrkrInstance
    CODESTARTcreateWrkrInstance;
    pWrkrData->snmpsession = NULL;
ENDcreateWrkrInstance

BEGINdbgPrintInstInfo
    CODESTARTdbgPrintInstInfo;
    dbgprintf("SNMPTransport: %s\n", pData->szTransport);
    dbgprintf("SNMPTarget: %s\n", pData->szTarget);
    dbgprintf("SNMPPort: %d\n", pData->iPort);
    dbgprintf("SNMPVersion (0=v1, 1=v2c): %d\n", pData->iSNMPVersion);
    dbgprintf("Community: %s\n", pData->szCommunity);
    dbgprintf("EnterpriseOID: %s\n", pData->szEnterpriseOID);
    dbgprintf("SnmpTrapOID: %s\n", pData->szSnmpTrapOID);
    dbgprintf("SyslogMessageOID: %s\n", pData->szSyslogMessageOID);
    dbgprintf("SnmpV1Source: %s\n", pData->szSnmpV1Source);
    dbgprintf("TrapType: %d\n", pData->iTrapType);
    dbgprintf("SpecificType: %d\n", pData->iSpecificType);
ENDdbgPrintInstInfo


BEGINisCompatibleWithFeature
    CODESTARTisCompatibleWithFeature;
    /* we are not compatible with repeated msg reduction feature, so do not allow it */
ENDisCompatibleWithFeature

/* Exit SNMP Session
 * alorbach, 2008-02-12
 */
static rsRetVal omsnmp_exitSession(wrkrInstanceData_t *pWrkrData) {
    DEFiRet;

    if (pWrkrData->snmpsession != NULL) {
        DBGPRINTF("omsnmp_exitSession: Clearing Session to '%s' on Port = '%d'\n", pWrkrData->pData->szTarget,
                  pWrkrData->pData->iPort);
        snmp_close(pWrkrData->snmpsession);
        pWrkrData->snmpsession = NULL;
    }

    RETiRet;
}

/* Init SNMP Session
 * alorbach, 2008-02-12
 */
static rsRetVal omsnmp_initSession(wrkrInstanceData_t *pWrkrData) {
    netsnmp_session session;
    instanceData *pData;
    char szTargetAndPort[MAXHOSTNAMELEN + 128]; /* work buffer for specifying a full target and port string */
    char *peername = NULL;
    unsigned char *community_dup = NULL;
    DEFiRet;

    /* should not happen, but if session is not cleared yet - we do it now! */
    if (pWrkrData->snmpsession != NULL) omsnmp_exitSession(pWrkrData);

    pData = pWrkrData->pData;

    snprintf((char *)szTargetAndPort, sizeof(szTargetAndPort), "%s:%s:%d",
             (pData->szTransport == NULL) ? "udp" : (char *)pData->szTransport, pData->szTarget,
             pData->iPort == 0 ? 162 : pData->iPort);

    dbgprintf("omsnmp_initSession: ENTER - Target = '%s' on Port = '%d'\n", pData->szTarget, pData->iPort);

    if (setenv("POSIXLY_CORRECT", "1", 1) == -1) ABORT_FINALIZE(RS_RET_ERR);

    snmp_sess_init(&session);
    session.version = pData->iSNMPVersion;
    session.callback = NULL; /* NOT NEEDED */
    session.callback_magic = NULL;
    /*
     * net-snmp's snmp_open() internally copies the session. However, we still
     * must not hand it a pointer to stack memory for peername/community
     * because ownership/copy behavior may vary between versions/builds.
     *
     * Provide heap-backed strings so the session open code can safely copy
     * from them.
     */
    CHKmalloc(peername = strdup(szTargetAndPort));
    session.peername = peername;

    /* Set SNMP Community */
    if (session.version == SNMP_VERSION_1 || session.version == SNMP_VERSION_2c) {
        const char *const community = (pData->szCommunity == NULL) ? "public" : (const char *)pData->szCommunity;
        CHKmalloc(community_dup = (unsigned char *)strdup(community));
        session.community = community_dup;
        session.community_len = strlen((char *)community_dup);
    }

    pWrkrData->snmpsession = snmp_open(&session);
    if (pWrkrData->snmpsession == NULL) {
        LogError(0, RS_RET_SUSPENDED,
                 "omsnmp_initSession: snmp_open to host '%s' on Port '%d' "
                 "failed\n",
                 pData->szTarget, pData->iPort);
        /* Stay suspended */
        iRet = RS_RET_SUSPENDED;
    }

finalize_it:
    /*
     * snmp_open() copies relevant session data; these scratch buffers are
     * no longer needed afterwards. If snmp_open() did not copy them for some
     * reason, the resulting problems will be detected quickly in tests.
     */
    free(peername);
    free(community_dup);
    RETiRet;
}

static rsRetVal omsnmp_sendsnmp(wrkrInstanceData_t *pWrkrData, uchar *psz, uchar *pszSource) {
    DEFiRet;

    netsnmp_pdu *pdu = NULL;
    oid enterpriseoid[MAX_OID_LEN];
    size_t enterpriseoidlen = MAX_OID_LEN;
    oid oidSyslogMessage[MAX_OID_LEN];
    size_t oLen = MAX_OID_LEN;
    int status;
    char *trap = NULL;
    const char *strErr = NULL;
    struct sockaddr_in srcAddr;
    instanceData *pData;

    pData = pWrkrData->pData;
    /* Init SNMP Session if necessary */
    if (pWrkrData->snmpsession == NULL) {
        CHKiRet(omsnmp_initSession(pWrkrData));
    }

    /* String should not be NULL */
    assert(psz != NULL);
    dbgprintf("omsnmp_sendsnmp: ENTER - Syslogmessage = '%s'\n", (char *)psz);

    /* If SNMP Version1 is configured !*/
    if (pWrkrData->snmpsession->version == SNMP_VERSION_1) {
        dbgprintf("omsnmp_sendsnmp: Create SNMPv1 Trap - Source = '%s'\n", (char *)pszSource);
        pdu = snmp_pdu_create(SNMP_MSG_TRAP);

        /* Set enterprise */
        if (!snmp_parse_oid(pData->szEnterpriseOID == NULL ? "1.3.6.1.4.1.3.1.1" : (char *)pData->szEnterpriseOID,
                            enterpriseoid, &enterpriseoidlen)) {
            strErr = snmp_api_errstring(snmp_errno);
            LogError(0, RS_RET_DISABLE_ACTION,
                     "omsnmp_sendsnmp: Parsing EnterpriseOID "
                     "failed '%s' with error '%s' \n",
                     pData->szSyslogMessageOID, strErr);
            ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
        }
        CHKmalloc(pdu->enterprise = (oid *)malloc(enterpriseoidlen * sizeof(oid)));
        memcpy(pdu->enterprise, enterpriseoid, enterpriseoidlen * sizeof(oid));
        pdu->enterprise_length = enterpriseoidlen;

        /* Set Traptype */
        pdu->trap_type = pData->iTrapType;

        /* Set SpecificType */
        pdu->specific_type = pData->iSpecificType;

        /* Set Updtime */
        pdu->time = get_uptime();

        /* Set PDU SOurce property if available */
        if (pszSource != NULL) {
            if (inet_pton(AF_INET, (const char *)pszSource, &srcAddr.sin_addr) == 1) {
                uint32_t s_addr = ntohl(srcAddr.sin_addr.s_addr);
                pdu->agent_addr[0] = (s_addr) & 0xFF;
                pdu->agent_addr[1] = (s_addr >> 8) & 0xFF;
                pdu->agent_addr[2] = (s_addr >> 16) & 0xFF;
                pdu->agent_addr[3] = (s_addr >> 24) & 0xFF;

                dbgprintf("omsnmp_sendsnmp: SNMPv1 Source Property set to %d.%d.%d.%d\n", pdu->agent_addr[0],
                          pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]);
            } else {
                LogError(0, NO_ERRCODE,
                         "omsnmp_sendsnmp: Failed to convert '%s' into a valid IPv4 "
                         "address\n",
                         pszSource);
            }
        }
    }
    /* If SNMP Version2c is configured !*/
    else if (pWrkrData->snmpsession->version == SNMP_VERSION_2c) {
        long sysuptime;
        char csysuptime[20];
        dbgprintf("omsnmp_sendsnmp: Create SNMPv2 Trap\n");

        /* Create PDU */
        pdu = snmp_pdu_create(SNMP_MSG_TRAP2);

        /* Set uptime */
        sysuptime = get_uptime();
        snprintf(csysuptime, sizeof(csysuptime), "%ld", sysuptime);
        trap = csysuptime;
        snmp_add_var(pdu, objid_sysuptime, sizeof(objid_sysuptime) / sizeof(oid), 't', trap);

        /* Now set the SyslogMessage Trap OID */
        if (snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o',
                         pData->szSnmpTrapOID == NULL ? "1.3.6.1.4.1.19406.1.2.1" : (char *)pData->szSnmpTrapOID) !=
            0) {
            strErr = snmp_api_errstring(snmp_errno);
            LogError(0, RS_RET_DISABLE_ACTION,
                     "omsnmp_sendsnmp: Adding trap OID failed '%s' "
                     "with error '%s' \n",
                     pData->szSnmpTrapOID, strErr);
            ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
        }
    }

    /* SET TRAP PARAMETER for SyslogMessage! */

    /* First create new OID object */
    if (snmp_parse_oid(
            pData->szSyslogMessageOID == NULL ? "1.3.6.1.4.1.19406.1.1.2.1" : (char *)pData->szSyslogMessageOID,
            oidSyslogMessage, &oLen)) {
        int iErrCode = snmp_add_var(pdu, oidSyslogMessage, oLen, 's', (char *)psz);
        if (iErrCode) {
            const char *str = snmp_api_errstring(iErrCode);
            LogError(0, RS_RET_DISABLE_ACTION,
                     "omsnmp_sendsnmp: Invalid SyslogMessage OID, "
                     "error code '%d' - '%s'\n",
                     iErrCode, str);
            ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
        }
    } else {
        strErr = snmp_api_errstring(snmp_errno);
        LogError(0, RS_RET_DISABLE_ACTION,
                 "omsnmp_sendsnmp: Parsing SyslogMessageOID failed '%s' "
                 "with error '%s' \n",
                 pData->szSyslogMessageOID, strErr);

        ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
    }

    /* Send the TRAP */
    status = snmp_send(pWrkrData->snmpsession, pdu) == 0;
    if (status) {
        /* Debug Output! */
        int iErrorCode = pWrkrData->snmpsession->s_snmp_errno;
        LogError(0, RS_RET_SUSPENDED,
                 "omsnmp_sendsnmp: snmp_send failed error '%d', "
                 "Description='%s'\n",
                 iErrorCode * (-1), api_errors[iErrorCode * (-1)]);

        /* Clear Session */
        omsnmp_exitSession(pWrkrData);

        ABORT_FINALIZE(RS_RET_SUSPENDED);
    } else {
        dbgprintf("omsnmp_sendsnmp: Successfully send SNMP Trap to %s:%d\n", pData->szTarget, pData->iPort);
    }

finalize_it:
    if (iRet != RS_RET_OK) {
        if (pdu != NULL) {
            snmp_free_pdu(pdu);
        }
    }

    dbgprintf("omsnmp_sendsnmp: LEAVE\n");
    RETiRet;
}


BEGINtryResume
    CODESTARTtryResume;
    iRet = omsnmp_initSession(pWrkrData);
ENDtryResume

BEGINdoAction
    CODESTARTdoAction;
    /* Abort if the STRING is not set, should never happen */
    if (ppString[0] == NULL) {
        ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
    }

    /* This will generate and send the SNMP Trap */
    iRet = omsnmp_sendsnmp(pWrkrData, ppString[0], ppString[1]);
finalize_it:
ENDdoAction

BEGINfreeInstance
    CODESTARTfreeInstance;
    free(pData->tplName);
    free(pData->szTarget);
    free(pData->szTransport);
    free(pData->szCommunity);
    free(pData->szEnterpriseOID);
    free(pData->szSnmpTrapOID);
    free(pData->szSyslogMessageOID);
    free(pData->szSnmpV1Source);
ENDfreeInstance

BEGINfreeWrkrInstance
    CODESTARTfreeWrkrInstance;
    omsnmp_exitSession(pWrkrData);
ENDfreeWrkrInstance

static void setInstParamDefaults(instanceData *pData) {
    pData->tplName = NULL;
    pData->szTransport = NULL;
    pData->szCommunity = NULL;
    pData->szEnterpriseOID = NULL;
    pData->szSnmpTrapOID = NULL;
    pData->szSyslogMessageOID = NULL;
    pData->szSnmpV1Source = NULL;
}

BEGINnewActInst
    struct cnfparamvals *pvals;
    int i;
    CODESTARTnewActInst;
    if ((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
        ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
    }

    CHKiRet(createInstance(&pData));
    setInstParamDefaults(pData);

    CODE_STD_STRING_REQUESTnewActInst(2);
    for (i = 0; i < actpblk.nParams; ++i) {
        if (!pvals[i].bUsed) continue;
        if (!strcmp(actpblk.descr[i].name, "server")) {
            CHKmalloc(pData->szTarget = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "port")) {
            pData->iPort = pvals[i].val.d.n;
        } else if (!strcmp(actpblk.descr[i].name, "transport")) {
            CHKmalloc(pData->szTransport = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "version")) {
            pData->iSNMPVersion = pvals[i].val.d.n;
            if (pData->iSNMPVersion < 0 || cs.iSNMPVersion > 1) pData->iSNMPVersion = 1;
        } else if (!strcmp(actpblk.descr[i].name, "community")) {
            CHKmalloc(pData->szCommunity = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "enterpriseoid")) {
            CHKmalloc(pData->szEnterpriseOID = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "trapoid")) {
            CHKmalloc(pData->szSnmpTrapOID = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "messageoid")) {
            CHKmalloc(pData->szSyslogMessageOID = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "snmpv1dynsource")) {
            CHKmalloc(pData->szSnmpV1Source = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else if (!strcmp(actpblk.descr[i].name, "traptype")) {
            pData->iTrapType = pvals[i].val.d.n;
            if (cs.iTrapType < 0 || cs.iTrapType > 6) {
                parser_errmsg("omsnmp: traptype invalid, setting to ENTERPRISESPECIFIC");
                pData->iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
            }
        } else if (!strcmp(actpblk.descr[i].name, "specifictype")) {
            pData->iSpecificType = pvals[i].val.d.n;
        } else if (!strcmp(actpblk.descr[i].name, "template")) {
            CHKmalloc(pData->tplName = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
        } else {
            dbgprintf(
                "ompipe: program error, non-handled "
                "param '%s'\n",
                actpblk.descr[i].name);
        }
    }

    /* Init NetSNMP library and read in MIB database */
    init_snmp("rsyslog");

    /* Set some defaults in the NetSNMP library */
    netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT, pData->iPort);

    uchar *tpl_msg = NULL;
    uchar *tpl_v1src = NULL;

    CHKmalloc(tpl_msg = (uchar *)strdup((pData->tplName == NULL) ? "RSYSLOG_FileFormat" : (char *)pData->tplName));
    iRet = OMSRsetEntry(*ppOMSR, 0, tpl_msg, OMSR_NO_RQD_TPL_OPTS);
    if (iRet != RS_RET_OK) {
        free(tpl_msg);
        tpl_msg = NULL;
        ABORT_FINALIZE(iRet);
    }
    /* ownership transferred to OMSR */
    tpl_msg = NULL;

    CHKmalloc(tpl_v1src = (uchar *)strdup((pData->szSnmpV1Source == NULL) ? " SNMP_SOURCETEMPLATE"
                                                                          : (char *)pData->szSnmpV1Source));
    iRet = OMSRsetEntry(*ppOMSR, 1, tpl_v1src, OMSR_NO_RQD_TPL_OPTS);
    if (iRet != RS_RET_OK) {
        free(tpl_v1src);
        tpl_v1src = NULL;
        ABORT_FINALIZE(iRet);
    }
    /* ownership transferred to OMSR */
    tpl_v1src = NULL;

    CODE_STD_FINALIZERnewActInst;
    cnfparamvalsDestruct(pvals, &actpblk);
ENDnewActInst


BEGINparseSelectorAct
    CODESTARTparseSelectorAct;
    CODE_STD_STRING_REQUESTparseSelectorAct(2) if (!strncmp((char *)p, ":omsnmp:", sizeof(":omsnmp:") - 1)) {
        p += sizeof(":omsnmp:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
    }
    else {
        ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
    }

    /* ok, if we reach this point, we have something for us */
    if ((iRet = createInstance(&pData)) != RS_RET_OK) FINALIZE;
    setInstParamDefaults(pData);

    /* Check Target */
    if (cs.pszTarget == NULL) {
        ABORT_FINALIZE(RS_RET_PARAM_ERROR);
    } else {
        CHKmalloc(pData->szTarget = (uchar *)strdup((char *)cs.pszTarget));
    }

    /* copy config params */
    pData->szTransport = (uchar *)((cs.pszTransport == NULL) ? NULL : strdup((char *)cs.pszTransport));
    pData->szCommunity = (uchar *)((cs.pszCommunity == NULL) ? NULL : strdup((char *)cs.pszCommunity));
    pData->szEnterpriseOID = (uchar *)((cs.pszEnterpriseOID == NULL) ? NULL : strdup((char *)cs.pszEnterpriseOID));
    pData->szSnmpTrapOID = (uchar *)((cs.pszSnmpTrapOID == NULL) ? NULL : strdup((char *)cs.pszSnmpTrapOID));
    pData->szSyslogMessageOID =
        (uchar *)((cs.pszSyslogMessageOID == NULL) ? NULL : strdup((char *)cs.pszSyslogMessageOID));
    pData->iPort = cs.iPort;
    pData->iSpecificType = cs.iSpecificType;

    /* Set SNMPVersion */
    if (cs.iSNMPVersion < 0 || cs.iSNMPVersion > 1) /* Set default to 1 if out of range */
        pData->iSNMPVersion = 1;
    else
        pData->iSNMPVersion = cs.iSNMPVersion;

    /* Copy TrapType */
    if (cs.iTrapType < 0 || cs.iTrapType >= 6) /* Only allow values from 0 to 6 !*/
        pData->iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
    else
        pData->iTrapType = cs.iTrapType;

    /* Print Debug info */
    dbgprintf("SNMPTransport: %s\n", pData->szTransport);
    dbgprintf("SNMPTarget: %s\n", pData->szTarget);
    dbgprintf("SNMPPort: %d\n", pData->iPort);
    dbgprintf("SNMPVersion (0=v1, 1=v2c): %d\n", pData->iSNMPVersion);
    dbgprintf("Community: %s\n", pData->szCommunity);
    dbgprintf("EnterpriseOID: %s\n", pData->szEnterpriseOID);
    dbgprintf("SnmpTrapOID: %s\n", pData->szSnmpTrapOID);
    dbgprintf("SyslogMessageOID: %s\n", pData->szSyslogMessageOID);
    dbgprintf("SnmpV1Source: %s\n", pData->szSnmpV1Source);
    dbgprintf("TrapType: %d\n", pData->iTrapType);
    dbgprintf("SpecificType: %d\n", pData->iSpecificType);

    /* Init NetSNMP library and read in MIB database */
    init_snmp("rsyslog");

    /* Set some defaults in the NetSNMP library */
    netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT, pData->iPort);

    /* process template */
    CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar *)"RSYSLOG_TraditionalForwardFormat"));

    /* Set up second template for SNMPv1 source (same as newActInst) */
    uchar *tpl_v1src = NULL;
    CHKmalloc(tpl_v1src = (uchar *)strdup((pData->szSnmpV1Source == NULL) ? " SNMP_SOURCETEMPLATE"
                                                                          : (char *)pData->szSnmpV1Source));
    iRet = OMSRsetEntry(*ppOMSR, 1, tpl_v1src, OMSR_NO_RQD_TPL_OPTS);
    if (iRet != RS_RET_OK) {
        free(tpl_v1src);
        tpl_v1src = NULL;
        ABORT_FINALIZE(iRet);
    }
    /* ownership transferred to OMSR */
    tpl_v1src = NULL;
    CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct


/* Reset config variables for this module to default values.
 */
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) * pp, void __attribute__((unused)) * pVal) {
    DEFiRet;
    free(cs.pszTarget);
    cs.pszTarget = NULL;
    free(cs.pszCommunity);
    cs.pszCommunity = NULL;
    free(cs.pszEnterpriseOID);
    cs.pszEnterpriseOID = NULL;
    free(cs.pszSnmpTrapOID);
    cs.pszSnmpTrapOID = NULL;
    free(cs.pszSyslogMessageOID);
    cs.pszSyslogMessageOID = NULL;
    cs.iPort = 0;
    cs.iSNMPVersion = 1;
    cs.iSpecificType = 0;
    cs.iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
    RETiRet;
}


BEGINmodExit
    CODESTARTmodExit;
    free(cs.pszTarget);
    free(cs.pszCommunity);
    free(cs.pszEnterpriseOID);
    free(cs.pszSnmpTrapOID);
    free(cs.pszSyslogMessageOID);

    /* release what we no longer need */
ENDmodExit


BEGINqueryEtryPt
    CODESTARTqueryEtryPt;
    CODEqueryEtryPt_STD_OMOD_QUERIES;
    CODEqueryEtryPt_STD_OMOD8_QUERIES;
    CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES;
    CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES;
ENDqueryEtryPt


BEGINmodInit()
    CODESTARTmodInit;
    uchar *pTmp;
    *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
    CODEmodInit_QueryRegCFSLineHdlr initConfVars();

    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptransport", 0, eCmdHdlrGetWord, NULL, &cs.pszTransport,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptarget", 0, eCmdHdlrGetWord, NULL, &cs.pszTarget,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(
        omsdRegCFSLineHdlr((uchar *)"actionsnmptargetport", 0, eCmdHdlrInt, NULL, &cs.iPort, STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpversion", 0, eCmdHdlrInt, NULL, &cs.iSNMPVersion,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpcommunity", 0, eCmdHdlrGetWord, NULL, &cs.pszCommunity,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpenterpriseoid", 0, eCmdHdlrGetWord, NULL, &cs.pszEnterpriseOID,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptrapoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSnmpTrapOID,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpsyslogmessageoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSyslogMessageOID,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpspecifictype", 0, eCmdHdlrInt, NULL, &cs.iSpecificType,
                               STD_LOADABLE_MODULE_ID));
    CHKiRet(
        omsdRegCFSLineHdlr((uchar *)"actionsnmptraptype", 0, eCmdHdlrInt, NULL, &cs.iTrapType, STD_LOADABLE_MODULE_ID));
    CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL,
                               STD_LOADABLE_MODULE_ID));

    DBGPRINTF("omsnmp: Add SNMP_SOURCETEMPLATE to template system ONCE\n");
    pTmp = (uchar *)SNMP_SOURCETEMPLATE;
    tplAddLine(ourConf, " SNMP_SOURCETEMPLATE", &pTmp);
ENDmodInit
/*
 * vi:set ai:
 */
