/* mmsnmptrapd.c
* This is a message modification module. It takes messages generated
* from snmptrapd and modifies them so that the look like they
* originated from the real originator.
*
* NOTE: read comments in module-template.h for details on the calling interface!
*
* File begun on 2011-05-05 by RGerhards
*
* Copyright 2011 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
* Rsyslog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rsyslog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rsyslog. If not, see .
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
#include "rsyslog.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "conf.h"
#include "msg.h"
#include "syslogd-types.h"
#include "template.h"
#include "module-template.h"
#include "errmsg.h"
#include "cfsysline.h"
#include "unicode-helper.h"
#include "dirty.h"
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
MODULE_CNFNAME("mmsnmptrapd")
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
/* static data */
DEFobjCurrIf(errmsg);
/* internal structures
*/
DEF_OMOD_STATIC_DATA
struct severMap_s {
uchar *name;
int code;
struct severMap_s *next;
};
typedef struct _instanceData {
uchar *pszTagName;
uchar *pszTagID; /* cached: name plus trailing shlash (for compares) */
int lenTagID; /* cached: length of tag ID, for performance reasons */
struct severMap_s *severMap;
} instanceData;
typedef struct wrkrInstanceData {
instanceData *pData;
} wrkrInstanceData_t;
typedef struct configSettings_s {
uchar *pszTagName; /**< name of tag start value that indicates snmptrapd initiated message */
uchar *pszSeverityMapping; /**< severitystring to numerical code mapping for snmptrapd string */
} configSettings_t;
static configSettings_t cs;
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
cs.pszTagName = NULL;
cs.pszSeverityMapping = NULL;
resetConfigVariables(NULL, NULL);
ENDinitConfVars
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
BEGINcreateWrkrInstance
CODESTARTcreateWrkrInstance
ENDcreateWrkrInstance
BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature
ENDisCompatibleWithFeature
BEGINfreeInstance
struct severMap_s *node, *nodeDel;
CODESTARTfreeInstance
for(node = pData->severMap ; node != NULL ; ) {
nodeDel = node;
node = node->next;
free(nodeDel->name);
free(nodeDel);
}
free(pData->pszTagName);
free(pData->pszTagID);
ENDfreeInstance
BEGINfreeWrkrInstance
CODESTARTfreeWrkrInstance
ENDfreeWrkrInstance
BEGINdbgPrintInstInfo
CODESTARTdbgPrintInstInfo
dbgprintf("mmsnmptrapd\n");
ENDdbgPrintInstInfo
BEGINtryResume
CODESTARTtryResume
ENDtryResume
/* check if a string is numeric (int) */
static inline int
isNumeric(uchar *str)
{
int r = 1;
if(*str == '-' || *str == '+')
++str;
while(*str) {
if(!isdigit(*str)) {
r = 0;
goto done;
}
++str;
}
done:
return r;
}
/* get a substring delimited by a character (or end of string). The
* string is trimmed, that is leading and trailing spaces are removed.
* The caller must provide a buffer which shall receive the substring.
* String length is returned as result. The input string is updated
* on exit, so that it may be used for another query starting at that
* position.
*/
static int
getSubstring(uchar **psrc, uchar delim, uchar *dst, int lenDst)
{
uchar *dstwrk = dst;
uchar *src = *psrc;
while(*src && isspace(*src)) {
++src; /* trim leading spaces */
}
while(*src && *src != delim && --lenDst > 0) {
*dstwrk++ = *src++;
}
dstwrk--;
while(dstwrk > dst && isspace(*dst))
--dstwrk; /* trim trailing spaces */
*++dstwrk = '\0';
/* final results */
if(*src == delim)
++src;
*psrc = src;
return(dstwrk - dst);
}
/* get string up to the next SP or '/'. Stops at max size.
* dst, lenDst (receive buffer) must be given. lenDst is
* max length on entry and actual length on exit.
*/
static int
getTagComponent(uchar *tag, uchar *dst, int *lenDst)
{
int end = *lenDst - 1; /* -1 for NUL-char! */
int i;
i = 0;
if(tag[i] != '/')
goto done;
++tag;
while(i < end && tag[i] != '\0' && tag[i] != ' ' && tag[i] != '/') {
dst[i] = tag[i];
++i;
}
dst[i] = '\0';
*lenDst = i;
done:
return i;
}
/* lookup severity code based on provided severity
* returns -1 if severity could not be found.
*/
static inline int
lookupSeverityCode(instanceData *pData, uchar *sever)
{
struct severMap_s *node;
int sevCode = -1;
for(node = pData->severMap ; node != NULL ; node = node->next) {
if(!ustrcmp(node->name, sever)) {
sevCode = node->code;
break;
}
}
return sevCode;
}
BEGINdoAction
int lenTAG;
int lenSever;
int lenHost;
int sevCode;
msg_t *pMsg;
uchar *pszTag;
uchar pszSever[512];
uchar pszHost[512];
instanceData *pData;
CODESTARTdoAction
pData = pWrkrData->pData;
pMsg = (msg_t*) ppString[0];
getTAG(pMsg, &pszTag, &lenTAG);
if(strncmp((char*)pszTag, (char*)pData->pszTagID, pData->lenTagID)) {
DBGPRINTF("tag '%s' not matching, mmsnmptrapd ignoring this message\n",
pszTag);
FINALIZE;
}
lenSever = sizeof(pszSever);
getTagComponent(pszTag+pData->lenTagID-1, pszSever, &lenSever);
lenHost = sizeof(pszHost);
getTagComponent(pszTag+pData->lenTagID+lenSever, pszHost, &lenHost);
DBGPRINTF("mmsnmptrapd: sever '%s'(%d), host '%s'(%d)\n", pszSever, lenSever, pszHost,lenHost);
if(pszHost[lenHost-1] == ':') {
pszHost[lenHost-1] = '\0';
--lenHost;
}
sevCode = lookupSeverityCode(pData, pszSever);
/* now apply new settings */
MsgSetTAG(pMsg, pData->pszTagName, pData->lenTagID);
MsgSetHOSTNAME(pMsg, pszHost, lenHost);
if(sevCode != -1)
pMsg->iSeverity = sevCode; /* we update like the parser does! */
finalize_it:
ENDdoAction
/* Build the severity mapping table based on user-provided configuration
* settings.
*/
static inline rsRetVal
buildSeverityMapping(instanceData *pData)
{
uchar pszSev[512];
uchar pszSevCode[512];
int sevCode;
uchar *mapping;
struct severMap_s *node;
DEFiRet;
mapping = cs.pszSeverityMapping;
while(1) { /* broken inside when all entries are processed */
if(getSubstring(&mapping, '/', pszSev, sizeof(pszSev)) == 0) {
FINALIZE;
}
if(getSubstring(&mapping, ',', pszSevCode, sizeof(pszSevCode)) == 0) {
errmsg.LogError(0, RS_RET_ERR, "error: invalid severity mapping, cannot "
"extract code. given: '%s'\n", cs.pszSeverityMapping);
ABORT_FINALIZE(RS_RET_ERR);
}
sevCode = atoi((char*) pszSevCode);
if(!isNumeric(pszSevCode))
sevCode = -1;
if(sevCode < 0 || sevCode > 7) {
errmsg.LogError(0, RS_RET_ERR, "error: severity code %d outside of valid "
"range 0..7 (was string '%s')\n", sevCode, pszSevCode);
ABORT_FINALIZE(RS_RET_ERR);
}
CHKmalloc(node = MALLOC(sizeof(struct severMap_s)));
CHKmalloc(node->name = ustrdup(pszSev));
node->code = sevCode;
/* we enqueue at the top, so the two lines below do all we need! */
node->next = pData->severMap;
pData->severMap = node;
DBGPRINTF("mmsnmptrapd: severity string '%s' mapped to code %d\n",
pszSev, sevCode);
}
finalize_it:
RETiRet;
}
BEGINparseSelectorAct
CODESTARTparseSelectorAct
CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* first check if this config line is actually for us */
if(strncmp((char*) p, ":mmsnmptrapd:", sizeof(":mmsnmptrapd:") - 1)) {
ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
}
/* ok, if we reach this point, we have something for us */
p += sizeof(":mmsnmptrapd:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
CHKiRet(createInstance(&pData));
/* check if a non-standard template is to be applied */
if(*(p-1) == ';')
--p;
/* we call the function below because we need to call it via our interface definition. However,
* the format specified (if any) is always ignored.
*/
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat"));
/* finally build the instance */
if(cs.pszTagName == NULL) {
pData->pszTagName = (uchar*) strdup("snmptrapd:");
pData->pszTagID = (uchar*) strdup("snmptrapd/");
} else {
int lenTag = ustrlen(cs.pszTagName);
/* new tag value (with colon at the end) */
CHKmalloc(pData->pszTagName = MALLOC(lenTag + 2));
memcpy(pData->pszTagName, cs.pszTagName, lenTag);
memcpy(pData->pszTagName+lenTag, ":", 2);
/* tag ID for comparisions */
CHKmalloc(pData->pszTagID = MALLOC(lenTag + 2));
memcpy(pData->pszTagID, cs.pszTagName, lenTag);
memcpy(pData->pszTagID+lenTag, "/", 2);
free(cs.pszTagName); /* no longer needed */
}
pData->lenTagID = ustrlen(pData->pszTagID);
if(cs.pszSeverityMapping != NULL) {
CHKiRet(buildSeverityMapping(pData));
}
/* all config vars auto-reset! */
cs.pszTagName = NULL;
free(cs.pszSeverityMapping);
cs.pszSeverityMapping = NULL;
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
BEGINmodExit
CODESTARTmodExit
objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
CODEqueryEtryPt_STD_OMOD8_QUERIES
CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES
ENDqueryEtryPt
/* Reset config variables for this module to default values.
*/
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
free(cs.pszTagName);
cs.pszTagName = NULL;
free(cs.pszSeverityMapping);
cs.pszSeverityMapping = NULL;
RETiRet;
}
BEGINmodInit()
rsRetVal localRet;
rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
unsigned long opts;
int bMsgPassingSupported;
CODESTARTmodInit
INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION;
/* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
/* check if the rsyslog core supports parameter passing code */
bMsgPassingSupported = 0;
localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts",
&pomsrGetSupportedTplOpts);
if(localRet == RS_RET_OK) {
/* found entry point, so let's see if core supports msg passing */
CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
if(opts & OMSR_TPL_AS_MSG)
bMsgPassingSupported = 1;
} else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */
}
if(!bMsgPassingSupported) {
DBGPRINTF("mmsnmptrapd: msg-passing is not supported by rsyslog core, "
"can not continue.\n");
ABORT_FINALIZE(RS_RET_NO_MSG_PASSING);
}
CHKiRet(objUse(errmsg, CORE_COMPONENT));
/* TODO: config vars ininit can be replaced by commented-out code above in v6 */
cs.pszTagName = NULL;
cs.pszSeverityMapping = NULL;
CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdtag", 0, eCmdHdlrGetWord,
NULL, &cs.pszTagName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdseveritymapping", 0, eCmdHdlrGetWord,
NULL, &cs.pszSeverityMapping, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vi:set ai:
*/