summaryrefslogtreecommitdiffstats
path: root/tcps_sess.c
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2008-03-14 17:03:14 +0000
committerRainer Gerhards <rgerhards@adiscon.com>2008-03-14 17:03:14 +0000
commitb0e8ce6c3d48e664135a0f74d5b4df2b3a42827f (patch)
treeb564ab5c477c194de8985d9c97a048b7fb95629c /tcps_sess.c
parent38362e127f7b7b836332bf17097dbbae71bbe810 (diff)
downloadrsyslog-b0e8ce6c3d48e664135a0f74d5b4df2b3a42827f.tar.gz
rsyslog-b0e8ce6c3d48e664135a0f74d5b4df2b3a42827f.tar.xz
rsyslog-b0e8ce6c3d48e664135a0f74d5b4df2b3a42827f.zip
bugfix: TCP (and GSSAPI) octet-counted frame did not work correctly in all
situations. If the header was split across two packet reads, it was invalidly processed, causing loss or modification of messages.
Diffstat (limited to 'tcps_sess.c')
-rw-r--r--tcps_sess.c193
1 files changed, 82 insertions, 111 deletions
diff --git a/tcps_sess.c b/tcps_sess.c
index c8ac3a37..2a6b300a 100644
--- a/tcps_sess.c
+++ b/tcps_sess.c
@@ -237,6 +237,86 @@ Close(tcps_sess_t *pThis)
}
+/* process the data received. As TCP is stream based, we need to process the
+ * data inside a state machine. The actual data received is passed in byte-by-byte
+ * from DataRcvd, and this function here compiles messages from them and submits
+ * the end result to the queue. Introducing this function fixes a long-term bug ;)
+ * rgerhards, 2008-03-14
+ */
+static rsRetVal
+processDataRcvd(tcps_sess_t *pThis, char c)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcps_sess);
+
+dbgprintf("processDataRcvd char %c, state %d\n", c, pThis->inputState);
+ if(pThis->inputState == eAtStrtFram) {
+ if(isdigit((int) c)) {
+ pThis->inputState = eInOctetCnt;
+ pThis->iOctetsRemain = 0;
+ pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
+ } else {
+ pThis->inputState = eInMsg;
+ pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
+ }
+ }
+
+ if(pThis->inputState == eInOctetCnt) {
+ if(isdigit(c)) {
+ pThis->iOctetsRemain = pThis->iOctetsRemain * 10 + c - '0';
+ } else { /* done with the octet count, so this must be the SP terminator */
+ dbgprintf("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
+ if(c != ' ') {
+ errmsg.LogError(NO_ERRCODE, "Framing Error in received TCP message: "
+ "delimiter is not SP but has ASCII value %d.\n", c);
+ }
+ if(pThis->iOctetsRemain < 1) {
+ /* TODO: handle the case where the octet count is 0! */
+ dbgprintf("Framing Error: invalid octet count\n");
+ errmsg.LogError(NO_ERRCODE, "Framing Error in received TCP message: "
+ "invalid octet count %d.\n", pThis->iOctetsRemain);
+ }
+ pThis->inputState = eInMsg;
+ }
+ } else {
+ assert(pThis->inputState == eInMsg);
+ if(pThis->iMsg >= MAXLINE) {
+ /* emergency, we now need to flush, no matter if we are at end of message or not... */
+ dbgprintf("error: message received is larger than MAXLINE, we split it\n");
+ parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG);
+ 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
+ * candidate for a configuration parameter...
+ * rgerhards, 2006-12-04
+ */
+ }
+
+ if(c == '\n' && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delemiter? */
+ parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG);
+ pThis->iMsg = 0;
+ pThis->inputState = eAtStrtFram;
+ } else {
+ /* IMPORTANT: here we copy the actual frame content to the message! */
+ *(pThis->msg + pThis->iMsg++) = c;
+ }
+
+ if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
+ /* do we need to find end-of-frame via octet counting? */
+ pThis->iOctetsRemain--;
+ if(pThis->iOctetsRemain < 1) {
+ /* we have end of frame! */
+ parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG);
+ pThis->iMsg = 0;
+ pThis->inputState = eAtStrtFram;
+ }
+ }
+ }
+
+ RETiRet;
+}
+
+
/* Processes the data received via a TCP session. If there
* is no other way to handle it, data is discarded.
* Input parameter data is the data received, iLen is its
@@ -244,16 +324,6 @@ Close(tcps_sess_t *pThis)
* is errors must be handled by caller!). iTCPSess must be
* the index of the TCP session that received the data.
* rgerhards 2005-07-04
- * Changed this functions interface. We now return a status of
- * what shall happen with the session. This is information for
- * the caller. If 1 is returned, the session should remain open
- * and additional data be accepted. If we return 0, the TCP
- * session is to be closed by the caller. This functionality is
- * needed in order to support framing errors, from which there
- * is no recovery possible other than session termination and
- * re-establishment. The need for this functionality thus is
- * primarily rooted in support for -transport-tls I-D framing.
- * rgerhards, 2006-12-07
* And another change while generalizing. We now return either
* RS_RET_OK, which means the session should be kept open
* or anything else, which means it must be closed.
@@ -263,7 +333,6 @@ static rsRetVal
DataRcvd(tcps_sess_t *pThis, char *pData, size_t iLen)
{
DEFiRet;
- register int iMsg;
char *pMsg;
char *pEnd;
@@ -284,112 +353,14 @@ DataRcvd(tcps_sess_t *pThis, char *pData, size_t iLen)
* - printline() the buffer
* - continue with copying
*/
- iMsg = pThis->iMsg; /* copy for speed */
pMsg = pThis->msg; /* just a shortcut */
pEnd = pData + iLen; /* this is one off, which is intensional */
while(pData < pEnd) {
- /* Check if we are at a new frame */
- if(pThis->bAtStrtOfFram) {
- /* we need to look at the message and detect
- * the framing mode used
- *//*
- * Contrary to -transport-tls, we accept leading zeros in the message
- * length. We do this in the spirit of "Be liberal in what you accept,
- * and conservative in what you send". We expect that including leading
- * zeros could be a common coding error.
- * rgerhards, 2006-12-07
- * The chairs of the IETF syslog-sec WG have announced that it is
- * consensus to do the octet count on the SYSLOG-MSG part only. I am
- * now changing the code to reflect this. Hopefully, it will not change
- * once again (there can no compatibility layer programmed for this).
- * To be on the save side, I just comment the code out. I mark these
- * comments with "IETF20061218".
- * rgerhards, 2006-12-19
- */
- if(isdigit((int) *pData)) {
- int iCnt; /* the frame count specified */
- pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
- /* in this mode, we have OCTET-COUNT SP MSG - so we now need
- * to extract the OCTET-COUNT and the SP and then extract
- * the msg.
- */
- iCnt = 0;
- /* IETF20061218 int iNbrOctets = 0; / * number of octets already consumed */
- while(isdigit((int) *pData)) {
- iCnt = iCnt * 10 + *pData - '0';
- /* IETF20061218 ++iNbrOctets; */
- ++pData;
- }
- dbgprintf("TCP Message with octet-counter, size %d.\n", iCnt);
- if(*pData == ' ') {
- ++pData; /* skip over SP */
- /* IETF20061218 ++iNbrOctets; */
- } else {
- /* TODO: handle "invalid frame" case */
- errmsg.LogError(NO_ERRCODE, "Framing Error in received TCP message: "
- "delimiter is not SP but has ASCII value %d.\n",
- *pData);
- return(0); /* unconditional error exit */
- }
- /* IETF20061218 pThis->iOctetsRemain = iCnt - iNbrOctets; */
- pThis->iOctetsRemain = iCnt;
- if(pThis->iOctetsRemain < 1) {
- /* TODO: handle the case where the octet count is 0 or negative! */
- dbgprintf("Framing Error: invalid octet count\n");
- errmsg.LogError(NO_ERRCODE, "Framing Error in received TCP message: "
- "invalid octet count %d.\n",
- pThis->iOctetsRemain);
- return(0); /* unconditional error exit */
- }
- } else {
- pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
- /* No need to do anything else here in this case */
- }
- pThis->bAtStrtOfFram = 0; /* done frame header */
- }
-
- /* now copy message until end of record */
-
- if(iMsg >= MAXLINE) {
- /* emergency, we now need to flush, no matter if
- * we are at end of message or not...
- */
- parseAndSubmitMessage(pThis->fromHost, pMsg, iMsg, MSG_PARSE_HOSTNAME, NOFLAG);
- 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
- * candidate for a configuration parameter...
- * rgerhards, 2006-12-04
- */
- }
-
- if(*pData == '\n' &&
- pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delemiter? */
- parseAndSubmitMessage(pThis->fromHost, pMsg, iMsg, MSG_PARSE_HOSTNAME, NOFLAG);
- iMsg = 0;
- pThis->bAtStrtOfFram = 1;
- ++pData;
- } else {
- /* IMPORTANT: here we copy the actual frame content to the message! */
- *(pMsg + iMsg++) = *pData++;
- }
-
- if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
- /* do we need to find end-of-frame via octet counting? */
- pThis->iOctetsRemain--;
- if(pThis->iOctetsRemain < 1) {
- /* we have end of frame! */
- parseAndSubmitMessage(pThis->fromHost, pMsg, iMsg, MSG_PARSE_HOSTNAME, NOFLAG);
- iMsg = 0;
- pThis->bAtStrtOfFram = 1;
- }
- }
+ CHKiRet(processDataRcvd(pThis, *pData++));
}
- pThis->iMsg = iMsg; /* persist value */
-
- return(1); /* successful return */
+finalize_it:
RETiRet;
}