mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-16 23:50:41 +01:00
* typo fix: ambigious -> ambiguous * typo fix: aquire -> acquire * typo fix: assgined -> assigned * typo fix: cancelation -> cancellation * typo fix: childs -> children * typo fix: configuraton -> configuration * typo fix: delemiter -> delimiter * typo fix: forwardig -> forwarding * typo fix: initializiation -> initialization * typo fix: intializing -> initializing * typo fix: lengh -> length * typo fix: mesage -> message * typo fix: occured -> occurred * typo fix: occurence -> occurrence * typo fix: paramter -> parameter * typo fix: remaing -> remaining * typo fix: resetted -> reset * typo fix: suppored -> supported * typo fix: Sytem -> System * typo fix: uncommited -> uncommitted * typo fix: depricated -> deprecated * typo fix: stoping -> stopping * type fix: allow to -> allow one to
420 lines
14 KiB
C
420 lines
14 KiB
C
/* pmrfc3164.c
|
|
* This is a parser module for RFC3164(legacy syslog)-formatted messages.
|
|
*
|
|
* NOTE: read comments in module-template.h to understand how this file
|
|
* works!
|
|
*
|
|
* File begun on 2009-11-04 by RGerhards
|
|
*
|
|
* Copyright 2007-2017 Rainer Gerhards and 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include "syslogd.h"
|
|
#include "conf.h"
|
|
#include "syslogd-types.h"
|
|
#include "template.h"
|
|
#include "msg.h"
|
|
#include "module-template.h"
|
|
#include "glbl.h"
|
|
#include "errmsg.h"
|
|
#include "parser.h"
|
|
#include "datetime.h"
|
|
#include "unicode-helper.h"
|
|
#include "rsconf.h"
|
|
MODULE_TYPE_PARSER
|
|
MODULE_TYPE_NOKEEP
|
|
PARSER_NAME("rsyslog.rfc3164")
|
|
MODULE_CNFNAME("pmrfc3164")
|
|
|
|
/* internal structures
|
|
*/
|
|
DEF_PMOD_STATIC_DATA
|
|
DEFobjCurrIf(glbl)
|
|
DEFobjCurrIf(parser)
|
|
DEFobjCurrIf(datetime)
|
|
|
|
|
|
/* static data */
|
|
static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
|
|
|
|
|
|
/* parser instance parameters */
|
|
static struct cnfparamdescr parserpdescr[] = {
|
|
{ "detect.yearaftertimestamp", eCmdHdlrBinary, 0 },
|
|
{ "permit.squarebracketsinhostname", eCmdHdlrBinary, 0 },
|
|
{ "permit.slashesinhostname", eCmdHdlrBinary, 0 },
|
|
{ "permit.atsignsinhostname", eCmdHdlrBinary, 0 },
|
|
{ "force.tagendingbycolon", eCmdHdlrBinary, 0},
|
|
{ "remove.msgfirstspace", eCmdHdlrBinary, 0},
|
|
};
|
|
static struct cnfparamblk parserpblk =
|
|
{ CNFPARAMBLK_VERSION,
|
|
sizeof(parserpdescr)/sizeof(struct cnfparamdescr),
|
|
parserpdescr
|
|
};
|
|
|
|
struct instanceConf_s {
|
|
int bDetectYearAfterTimestamp;
|
|
int bPermitSquareBracketsInHostname;
|
|
int bPermitSlashesInHostname;
|
|
int bPermitAtSignsInHostname;
|
|
int bForceTagEndingByColon;
|
|
int bRemoveMsgFirstSpace;
|
|
};
|
|
|
|
|
|
BEGINisCompatibleWithFeature
|
|
CODESTARTisCompatibleWithFeature
|
|
if(eFeat == sFEATUREAutomaticSanitazion)
|
|
iRet = RS_RET_OK;
|
|
if(eFeat == sFEATUREAutomaticPRIParsing)
|
|
iRet = RS_RET_OK;
|
|
ENDisCompatibleWithFeature
|
|
|
|
|
|
/* create input instance, set default parameters, and
|
|
* add it to the list of instances.
|
|
*/
|
|
static rsRetVal
|
|
createInstance(instanceConf_t **pinst)
|
|
{
|
|
instanceConf_t *inst;
|
|
DEFiRet;
|
|
CHKmalloc(inst = malloc(sizeof(instanceConf_t)));
|
|
inst->bDetectYearAfterTimestamp = 0;
|
|
inst->bPermitSquareBracketsInHostname = 0;
|
|
inst->bPermitSlashesInHostname = 0;
|
|
inst->bPermitAtSignsInHostname = 0;
|
|
inst->bForceTagEndingByColon = 0;
|
|
inst->bRemoveMsgFirstSpace = 0;
|
|
bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(loadConf);
|
|
*pinst = inst;
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
BEGINnewParserInst
|
|
struct cnfparamvals *pvals = NULL;
|
|
int i;
|
|
CODESTARTnewParserInst
|
|
DBGPRINTF("newParserInst (pmrfc3164)\n");
|
|
|
|
inst = NULL;
|
|
CHKiRet(createInstance(&inst));
|
|
|
|
if(lst == NULL)
|
|
FINALIZE; /* just set defaults, no param block! */
|
|
|
|
if((pvals = nvlstGetParams(lst, &parserpblk, NULL)) == NULL) {
|
|
ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
|
|
}
|
|
|
|
if(Debug) {
|
|
dbgprintf("parser param blk in pmrfc3164:\n");
|
|
cnfparamsPrint(&parserpblk, pvals);
|
|
}
|
|
|
|
for(i = 0 ; i < parserpblk.nParams ; ++i) {
|
|
if(!pvals[i].bUsed)
|
|
continue;
|
|
if(!strcmp(parserpblk.descr[i].name, "detect.yearaftertimestamp")) {
|
|
inst->bDetectYearAfterTimestamp = (int) pvals[i].val.d.n;
|
|
} else if(!strcmp(parserpblk.descr[i].name, "permit.squarebracketsinhostname")) {
|
|
inst->bPermitSquareBracketsInHostname = (int) pvals[i].val.d.n;
|
|
} else if(!strcmp(parserpblk.descr[i].name, "permit.slashesinhostname")) {
|
|
inst->bPermitSlashesInHostname = (int) pvals[i].val.d.n;
|
|
} else if(!strcmp(parserpblk.descr[i].name, "permit.atsignsinhostname")) {
|
|
inst->bPermitAtSignsInHostname = (int) pvals[i].val.d.n;
|
|
} else if(!strcmp(parserpblk.descr[i].name, "force.tagendingbycolon")) {
|
|
inst->bForceTagEndingByColon = (int) pvals[i].val.d.n;
|
|
} else if(!strcmp(parserpblk.descr[i].name, "remove.msgfirstspace")) {
|
|
inst->bRemoveMsgFirstSpace = (int) pvals[i].val.d.n;
|
|
} else {
|
|
dbgprintf("pmrfc3164: program error, non-handled "
|
|
"param '%s'\n", parserpblk.descr[i].name);
|
|
}
|
|
}
|
|
finalize_it:
|
|
CODE_STD_FINALIZERnewParserInst
|
|
if(lst != NULL)
|
|
cnfparamvalsDestruct(pvals, &parserpblk);
|
|
if(iRet != RS_RET_OK)
|
|
free(inst);
|
|
ENDnewParserInst
|
|
|
|
|
|
BEGINfreeParserInst
|
|
CODESTARTfreeParserInst
|
|
dbgprintf("pmrfc3164: free parser instance %p\n", pInst);
|
|
ENDfreeParserInst
|
|
|
|
|
|
/* parse a legay-formatted syslog message.
|
|
*/
|
|
BEGINparse2
|
|
uchar *p2parse;
|
|
int lenMsg;
|
|
int i; /* general index for parsing */
|
|
uchar bufParseTAG[CONF_TAG_MAXSIZE];
|
|
uchar bufParseHOSTNAME[CONF_HOSTNAME_MAXSIZE];
|
|
CODESTARTparse
|
|
assert(pMsg != NULL);
|
|
assert(pMsg->pszRawMsg != NULL);
|
|
lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI;
|
|
DBGPRINTF("Message will now be parsed by the legacy syslog parser (offAfterPRI=%d, lenMsg=%d.\n",
|
|
pMsg->offAfterPRI, lenMsg);
|
|
/* note: offAfterPRI is already the number of PRI chars (do not add one!) */
|
|
p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
|
|
setProtocolVersion(pMsg, MSG_LEGACY_PROTOCOL);
|
|
if(pMsg->iFacility == (LOG_INVLD>>3)) {
|
|
DBGPRINTF("facility LOG_INVLD, do not parse\n");
|
|
FINALIZE;
|
|
}
|
|
|
|
/* now check if we have a completely headerless message. This is indicated
|
|
* by spaces or tabs followed '{' or '['.
|
|
*/
|
|
i = 0;
|
|
while(i < lenMsg && (p2parse[i] == ' ' || p2parse[i] == '\t')) {
|
|
++i;
|
|
}
|
|
if(i < lenMsg && (p2parse[i] == '{' || p2parse[i] == '[')) {
|
|
DBGPRINTF("msg seems to be headerless, treating it as such\n");
|
|
FINALIZE;
|
|
}
|
|
|
|
|
|
/* Check to see if msg contains a timestamp. We start by assuming
|
|
* that the message timestamp is the time of reception (which we
|
|
* generated ourselfs and then try to actually find one inside the
|
|
* message. There we go from high-to low precison and are done
|
|
* when we find a matching one. -- rgerhards, 2008-09-16
|
|
*/
|
|
if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
|
|
/* we are done - parse pointer is moved by ParseTIMESTAMP3339 */;
|
|
} else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg,
|
|
NO_PARSE3164_TZSTRING, pInst->bDetectYearAfterTimestamp) == RS_RET_OK) {
|
|
if(pMsg->dfltTZ[0] != '\0')
|
|
applyDfltTZ(&pMsg->tTIMESTAMP, pMsg->dfltTZ);
|
|
/* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
|
|
} else if(*p2parse == ' ' && lenMsg > 1) {
|
|
/* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
|
|
++p2parse; /* move over space */
|
|
--lenMsg;
|
|
if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg,
|
|
NO_PARSE3164_TZSTRING, pInst->bDetectYearAfterTimestamp) == RS_RET_OK) {
|
|
/* indeed, we got it! */
|
|
/* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
|
|
} else {/* parse pointer needs to be restored, as we moved it off-by-one
|
|
* for this try.
|
|
*/
|
|
--p2parse;
|
|
++lenMsg;
|
|
}
|
|
}
|
|
|
|
if(pMsg->msgFlags & IGNDATE) {
|
|
/* we need to ignore the msg data, so simply copy over reception date */
|
|
memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
|
|
}
|
|
|
|
/* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we
|
|
* do this only when the user has not forbidden this. I now introduce some
|
|
* code that allows a user to configure rsyslogd to treat the rest of the
|
|
* message as MSG part completely. In this case, the hostname will be the
|
|
* machine that we received the message from and the tag will be empty. This
|
|
* is meant to be an interim solution, but for now it is in the code.
|
|
*/
|
|
if(bParseHOSTNAMEandTAG && !(pMsg->msgFlags & INTERNAL_MSG)) {
|
|
/* parse HOSTNAME - but only if this is network-received!
|
|
* rger, 2005-11-14: we still have a problem with BSD messages. These messages
|
|
* do NOT include a host name. In most cases, this leads to the TAG to be treated
|
|
* as hostname and the first word of the message as the TAG. Clearly, this is not
|
|
* of advantage ;) I think I have now found a way to handle this situation: there
|
|
* are certain characters which are frequently used in TAG (e.g. ':'), which are
|
|
* *invalid* in host names. So while parsing the hostname, I check for these characters.
|
|
* If I find them, I set a simple flag but continue. After parsing, I check the flag.
|
|
* If it was set, then we most probably do not have a hostname but a TAG. Thus, I change
|
|
* the fields. I think this logic shall work with any type of syslog message.
|
|
* rgerhards, 2009-06-23: and I now have extended this logic to every character
|
|
* that is not a valid hostname.
|
|
* A "hostname" can validly include "[]" at the beginning and end. This sometimes
|
|
* happens with IP address (e.g. "[192.168.0.1]"). This must be turned on via
|
|
* an option as it may interfere with non-hostnames in some message formats.
|
|
* rgerhards, 2015-04-20
|
|
*/
|
|
if(lenMsg > 0 && pMsg->msgFlags & PARSE_HOSTNAME) {
|
|
i = 0;
|
|
int bHadSBracket = 0;
|
|
if(pInst->bPermitSquareBracketsInHostname) {
|
|
assert(i < lenMsg);
|
|
if(p2parse[i] == '[') {
|
|
bHadSBracket = 1;
|
|
bufParseHOSTNAME[0] = '[';
|
|
++i;
|
|
}
|
|
}
|
|
while(i < lenMsg
|
|
&& (isalnum(p2parse[i]) || p2parse[i] == '.'
|
|
|| p2parse[i] == '_' || p2parse[i] == '-'
|
|
|| (p2parse[i] == ']' && bHadSBracket)
|
|
|| (p2parse[i] == '@' && pInst->bPermitAtSignsInHostname)
|
|
|| (p2parse[i] == '/' && pInst->bPermitSlashesInHostname) )
|
|
&& i < (CONF_HOSTNAME_MAXSIZE - 1)) {
|
|
bufParseHOSTNAME[i] = p2parse[i];
|
|
++i;
|
|
if(p2parse[i] == ']')
|
|
break; /* must be closing bracket */
|
|
}
|
|
|
|
if(i == lenMsg) {
|
|
/* we have a message that is empty immediately after the hostname,
|
|
* but the hostname thus is valid! -- rgerhards, 2010-02-22
|
|
*/
|
|
p2parse += i;
|
|
lenMsg -= i;
|
|
bufParseHOSTNAME[i] = '\0';
|
|
MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
|
|
} else {
|
|
int isHostName = 0;
|
|
if(i > 0) {
|
|
if(bHadSBracket) {
|
|
if(p2parse[i] == ']') {
|
|
bufParseHOSTNAME[i] = ']';
|
|
++i;
|
|
isHostName = 1;
|
|
}
|
|
} else {
|
|
if(isalnum(p2parse[i-1])) {
|
|
isHostName = 1;
|
|
}
|
|
}
|
|
if(p2parse[i] != ' ')
|
|
isHostName = 0;
|
|
}
|
|
|
|
if(isHostName) {
|
|
/* we got a hostname! */
|
|
p2parse += i + 1; /* "eat" it (including SP delimiter) */
|
|
lenMsg -= i + 1;
|
|
bufParseHOSTNAME[i] = '\0';
|
|
MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now parse TAG - that should be present in message from all sources.
|
|
* This code is somewhat not compliant with RFC 3164. As of 3164,
|
|
* the TAG field is ended by any non-alphanumeric character. In
|
|
* practice, however, the TAG often contains dashes and other things,
|
|
* which would end the TAG. So it is not desirable. As such, we only
|
|
* accept colon and SP to be terminators. Even there is a slight difference:
|
|
* a colon is PART of the TAG, while a SP is NOT part of the tag
|
|
* (it is CONTENT). Starting 2008-04-04, we have removed the 32 character
|
|
* size limit (from RFC3164) on the tag. This had bad effects on existing
|
|
* envrionments, as sysklogd didn't obey it either (probably another bug
|
|
* in RFC3164...). We now receive the full size, but will modify the
|
|
* outputs so that only 32 characters max are used by default.
|
|
*/
|
|
i = 0;
|
|
while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE - 2) {
|
|
bufParseTAG[i++] = *p2parse++;
|
|
--lenMsg;
|
|
}
|
|
if(lenMsg > 0 && *p2parse == ':') {
|
|
++p2parse;
|
|
--lenMsg;
|
|
bufParseTAG[i++] = ':';
|
|
}
|
|
else if (pInst->bForceTagEndingByColon) {
|
|
/* Tag need to be ended by a colon or it's not a tag but the
|
|
* begin of the message
|
|
*/
|
|
p2parse -= ( i + 1 );
|
|
lenMsg += ( i + 1 );
|
|
i = 0;
|
|
/* Default TAG is dash (without ':')
|
|
*/
|
|
bufParseTAG[i++] = '-';
|
|
}
|
|
|
|
/* no TAG can only be detected if the message immediatly ends, in which case an empty TAG
|
|
* is considered OK. So we do not need to check for empty TAG. -- rgerhards, 2009-06-23
|
|
*/
|
|
bufParseTAG[i] = '\0'; /* terminate string */
|
|
MsgSetTAG(pMsg, bufParseTAG, i);
|
|
} else {/* we enter this code area when the user has instructed rsyslog NOT
|
|
* to parse HOSTNAME and TAG - rgerhards, 2006-03-13
|
|
*/
|
|
if(!(pMsg->msgFlags & INTERNAL_MSG)) {
|
|
DBGPRINTF("HOSTNAME and TAG not parsed by user configuration.\n");
|
|
}
|
|
}
|
|
|
|
finalize_it:
|
|
if (pInst->bRemoveMsgFirstSpace && *p2parse == ' ') {
|
|
/* Bypass first space found in MSG part */
|
|
p2parse++;
|
|
lenMsg--;
|
|
}
|
|
MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg);
|
|
ENDparse2
|
|
|
|
|
|
BEGINmodExit
|
|
CODESTARTmodExit
|
|
/* release what we no longer need */
|
|
objRelease(glbl, CORE_COMPONENT);
|
|
objRelease(parser, CORE_COMPONENT);
|
|
objRelease(datetime, CORE_COMPONENT);
|
|
ENDmodExit
|
|
|
|
|
|
BEGINqueryEtryPt
|
|
CODESTARTqueryEtryPt
|
|
CODEqueryEtryPt_STD_PMOD2_QUERIES
|
|
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
|
|
ENDqueryEtryPt
|
|
|
|
|
|
BEGINmodInit(pmrfc3164)
|
|
CODESTARTmodInit
|
|
*ipIFVersProvided = CURR_MOD_IF_VERSION;
|
|
/* we only support the current interface specification */
|
|
CODEmodInit_QueryRegCFSLineHdlr
|
|
CHKiRet(objUse(glbl, CORE_COMPONENT));
|
|
CHKiRet(objUse(parser, CORE_COMPONENT));
|
|
CHKiRet(objUse(datetime, CORE_COMPONENT));
|
|
|
|
DBGPRINTF("rfc3164 parser init called\n");
|
|
bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(loadConf);
|
|
/* cache value, is set only during rsyslogd option processing */
|
|
|
|
|
|
ENDmodInit
|
|
|
|
/* vim:set ai:
|
|
*/
|