diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | doc/imtcp.html | 27 | ||||
-rw-r--r-- | plugins/imtcp/imtcp.c | 17 | ||||
-rw-r--r-- | tcps_sess.c | 12 | ||||
-rw-r--r-- | tcpsrv.c | 35 | ||||
-rw-r--r-- | tcpsrv.h | 19 |
6 files changed, 112 insertions, 7 deletions
@@ -1,3 +1,12 @@ +- added $InputTCPServerAddtlFrameDelimiter config directive, which + enabeles to specify an additional, non-standard message delimiter + for processing plain tcp syslog. This is primarily a fix for the invalid + framing used in Juniper's NetScreen products. Credit to forum user + Arv for suggesting this solution. +- added $InputTCPServerInputName property, which enables a name to be + specified that will be available during message processing in the + inputname property. This is considered useful for logic that treats + messages differently depending on which input received them. - added $PreserveFQDN config file directive Enables to use FQDNs in sender names where the legacy default would have stripped the domain part. diff --git a/doc/imtcp.html b/doc/imtcp.html index 583cd531..0ee0f96a 100644 --- a/doc/imtcp.html +++ b/doc/imtcp.html @@ -20,11 +20,36 @@ $InputTCPServerRun multiple times. This is not currently supported. </p> <p><b>Configuration Directives</b>:</p> <ul> +<li>$InputTCPServerAddtlFrameDelimiter <Delimiter><br> +This directive permits to specify an additional frame delimiter for plain tcp syslog. +The industry-standard specifies using the LF character as frame delimiter. Some vendors, +notable Juniper in their NetScreen products, use an invalid frame delimiter, in Juniper's +case the NUL character. This directive permits to specify the ASCII value of the delimiter +in question. Please note that this does not guarantee that all wrong implementations can +be cured with this directive. It is not even a sure fix with all versions of NetScreen, +as I suggest the NUL character is the effect of a (common) coding error and thus will +probably go away at some time in the future. But for the time being, the value 0 can +probably be used to make rsyslog handle NetScreen's invalid syslog/tcp framing. +For additional information, see this +<a href="http://kb.monitorware.com/problem-with-netscreen-log-t1652.html">forum thread</a>. +<br><b>If this doesn't work for you, please do not blame the rsyslog team. Instead file +a bug report with Juniper!</b> +<br>Note that a similar, but worse, issue exists with Cisco's IOS implementation. They do +not use any framing at all. This is confirmed from Cisco's side, but there seems to be +very limited interest in fixing this issue. This directive <b>can not</b> fix the Cisco bug. +That would require much more code changes, which I was unable to do so far. Full details +can be found at the <a href="http://www.rsyslog.com/Article321.phtml">Cisco tcp syslog anomaly</a> +page. <li>$InputTCPServerRun <port><br> Starts a TCP server on selected port</li> <li><ul><li>$InputTCPMaxSessions <number></li></ul> Sets the maximum number of sessions supported</li><li>$InputTCPServerStreamDriverMode <number><br> -Sets the driver mode for the currently selected <a href="netstream.html">network stream driver</a>. <number> is driver specifc.</li><li>$InputTCPServerStreamDriverAuthMode <mode-string><br> +Sets the driver mode for the currently selected <a href="netstream.html">network stream driver</a>. <number> is driver specifc.</li> +<li>$InputTCPServerInputName <name><br> +Sets a name for the inputname property. If no name is set "imtcp" is used by default. Setting a +name is not strictly necessary, but can be useful to apply filtering based on which input +the message was received from. +<li>$InputTCPServerStreamDriverAuthMode <mode-string><br> Sets the authentication mode for the currently selected <a href="netstream.html">network stream driver</a>. <mode-string> is driver specifc.</li><li>$InputTCPServerStreamDriverPermittedPeer <id-string><br> Sets permitted peer IDs. Only these peers are able to connect to the listener. <id-string> semantics depend on the currently selected diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c index 89f1dbcf..19138d94 100644 --- a/plugins/imtcp/imtcp.c +++ b/plugins/imtcp/imtcp.c @@ -80,7 +80,9 @@ static permittedPeers_t *pPermPeersRoot = NULL; /* config settings */ static int iTCPSessMax = 200; /* max number of sessions */ static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ +static int iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; /* addtl frame delimiter, e.g. for netscreen, default none */ static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ +static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */ /* callbacks */ @@ -166,6 +168,8 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose)); CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose)); CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode)); + CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ? (uchar*)"imtcp" : pszInputName)); + CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim)); /* now set optional params, but only if they were actually configured */ if(pszStrmDrvrAuthMode != NULL) { CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode)); @@ -239,6 +243,15 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus { iTCPSessMax = 200; iStrmDrvrMode = 0; + iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; + if(pszInputName != NULL) { + free(pszInputName); + pszInputName = NULL; + } + if(pszStrmDrvrAuthMode != NULL) { + free(pszStrmDrvrAuthMode); + pszStrmDrvrAuthMode = NULL; + } return RS_RET_OK; } @@ -273,6 +286,10 @@ CODEmodInit_QueryRegCFSLineHdlr eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverpermittedpeer", 0, eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserveraddtlframedelimiter", 0, eCmdHdlrInt, + NULL, &iAddtlFrameDelim, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverinputname", 0, + eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit diff --git a/tcps_sess.c b/tcps_sess.c index e8bef5b1..d0edc018 100644 --- a/tcps_sess.c +++ b/tcps_sess.c @@ -231,7 +231,7 @@ PrepareClose(tcps_sess_t *pThis) */ dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n"); parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg, - PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, NULL, NULL, 0); /* TODO: add real InputName */ + PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0); pThis->bAtStrtOfFram = 1; } @@ -314,7 +314,7 @@ processDataRcvd(tcps_sess_t *pThis, char c) /* emergency, we now need to flush, no matter if we are at end of message or not... */ dbgprintf("error: message received is larger than max msg size, we split it\n"); parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg, - PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, NULL, NULL, 0); /* TODO: add real InputName */ + PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0); pThis->iMsg = 0; /* we might think if it is better to ignore the rest of the * message than to treat it as a new one. Maybe this is a good @@ -323,9 +323,11 @@ processDataRcvd(tcps_sess_t *pThis, char c) */ } - if(c == '\n' && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delemiter? */ + if(( (c == '\n') + || ((pThis->pSrv->addtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->addtlFrameDelim)) + ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */ parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg, - PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, NULL, NULL, 0); /* TODO: add real InputName */ + PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0); pThis->iMsg = 0; pThis->inputState = eAtStrtFram; } else { @@ -344,7 +346,7 @@ processDataRcvd(tcps_sess_t *pThis, char c) if(pThis->iOctetsRemain < 1) { /* we have end of frame! */ parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg, - PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, NULL, NULL, 0); /* TODO: add real InputName */ + PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0); pThis->iMsg = 0; pThis->inputState = eAtStrtFram; } @@ -513,6 +513,7 @@ finalize_it: /* this is a very special case - this time only we do not exit the /* Standard-Constructor */ BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */ pThis->iSessMax = TCPSESS_MAX_DEFAULT; /* TODO: useful default ;) */ + pThis->addtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; ENDobjConstruct(tcpsrv) @@ -560,6 +561,8 @@ CODESTARTobjDestruct(tcpsrv) free(pThis->pszDrvrAuthMode); if(pThis->ppLstn != NULL) free(pThis->ppLstn); + if(pThis->pszInputName != NULL) + free(pThis->pszInputName); ENDobjDestruct(tcpsrv) @@ -658,6 +661,36 @@ SetUsrP(tcpsrv_t *pThis, void *pUsr) } +/* Set additional framing to use (if any) -- rgerhards, 2008-12-10 */ +static rsRetVal +SetAddtlFrameDelim(tcpsrv_t *pThis, int iDelim) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, tcpsrv); + pThis->addtlFrameDelim = iDelim; + RETiRet; +} + + +/* Set the input name to use -- rgerhards, 2008-12-10 */ +static rsRetVal +SetInputName(tcpsrv_t *pThis, uchar *name) +{ + uchar *pszName; + DEFiRet; + ISOBJ_TYPE_assert(pThis, tcpsrv); + if(name == NULL) + pszName = NULL; + else + CHKmalloc(pszName = (uchar*)strdup((char*)name)); + if(pThis->pszInputName != NULL) + free(pThis->pszInputName); + pThis->pszInputName = pszName; +finalize_it: + RETiRet; +} + + /* here follows a number of methods that shuffle authentication settings down * to the drivers. Drivers not supporting these settings may return an error * state. @@ -727,6 +760,8 @@ CODESTARTobjQueryInterface(tcpsrv) pIf->Run = Run; pIf->SetUsrP = SetUsrP; + pIf->SetInputName = SetInputName; + pIf->SetAddtlFrameDelim = SetAddtlFrameDelim; pIf->SetDrvrMode = SetDrvrMode; pIf->SetDrvrAuthMode = SetDrvrAuthMode; pIf->SetDrvrPermPeers = SetDrvrPermPeers; @@ -25,17 +25,28 @@ #include "obj.h" #include "tcps_sess.h" +/* support for framing anomalies */ +typedef enum ETCPsyslogFramingAnomaly { + frame_normal = 0, + frame_NetScreen = 1, + frame_CiscoIOS = 2 +} eTCPsyslogFramingAnomaly; + +#define TCPSRV_NO_ADDTL_DELIMITER -1 /* specifies that no additional delimiter is to be used in TCP framing */ + /* the tcpsrv object */ struct tcpsrv_s { BEGINobjInstance; /**< Data to implement generic object - MUST be the first data element! */ netstrms_t *pNS; /**< pointer to network stream subsystem */ int iDrvrMode; /**< mode of the stream driver to use */ uchar *pszDrvrAuthMode; /**< auth mode of the stream driver to use */ + uchar *pszInputName; /**< value to be used as input name */ permittedPeers_t *pPermPeers;/**< driver's permitted peers */ int iLstnMax; /**< max nbr of listeners currently supported */ netstrm_t **ppLstn; /**< our netstream listners */ int iSessMax; /**< max number of sessions supported */ char *TCPLstnPort; /**< the port the listener shall listen on */ + int addtlFrameDelim; /**< additional frame delimiter for plain TCP syslog framing (e.g. to handle NetScreen) */ tcps_sess_t **pSessions;/**< array of all of our sessions */ void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/ /* callbacks */ @@ -64,6 +75,8 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */ rsRetVal (*create_tcp_socket)(tcpsrv_t *pThis); rsRetVal (*Run)(tcpsrv_t *pThis); /* set methods */ + rsRetVal (*SetAddtlFrameDelim)(tcpsrv_t*, int); + rsRetVal (*SetInputName)(tcpsrv_t*, uchar*); rsRetVal (*SetUsrP)(tcpsrv_t*, void*); rsRetVal (*SetCBIsPermittedHost)(tcpsrv_t*, int (*) (struct sockaddr *addr, char*, void*, void*)); rsRetVal (*SetCBOpenLstnSocks)(tcpsrv_t *, rsRetVal (*)(tcpsrv_t*)); @@ -80,7 +93,11 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */ rsRetVal (*SetCBOnSessDestruct)(tcpsrv_t*, rsRetVal (*) (void*)); rsRetVal (*SetCBOnSessConstructFinalize)(tcpsrv_t*, rsRetVal (*) (void*)); ENDinterface(tcpsrv) -#define tcpsrvCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */ +#define tcpsrvCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */ +/* change for v4: + * - SetAddtlFrameDelim() added -- rgerhards, 2008-12-10 + * - SetInputName() added -- rgerhards, 2008-12-10 + */ /* prototypes */ |