~ubuntu-branches/ubuntu/karmic/rsyslog/karmic

1.1.2 by Michael Biebl
Import upstream version 3.14.2
1
/* tcps_sess.c
2
 *
3
 * This implements a session of the tcpsrv object. For general
4
 * comments, see header of tcpsrv.c.
5
 *
6
 * NOTE: read comments in module-template.h to understand how this file
7
 *       works!
8
 *
9
 * File begun on 2008-03-01 by RGerhards (extracted from tcpsrv.c)
10
 *
11
 * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
12
 *
13
 * This file is part of rsyslog.
14
 *
15
 * Rsyslog is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation, either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * Rsyslog is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with Rsyslog.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 * A copy of the GPL can be found in the file "COPYING" in this distribution.
29
 */
30
#include "config.h"
31
#include <stdlib.h>
1.2.7 by Michael Biebl
Import upstream version 4.2.0
32
#include <string.h>
1.1.2 by Michael Biebl
Import upstream version 3.14.2
33
#include <assert.h>
34
#include <errno.h>
35
#include <ctype.h>
1.2.4 by Michael Biebl
Import upstream version 3.20.4
36
1.1.2 by Michael Biebl
Import upstream version 3.14.2
37
#include "rsyslog.h"
1.2.4 by Michael Biebl
Import upstream version 3.20.4
38
#include "dirty.h"
1.1.2 by Michael Biebl
Import upstream version 3.14.2
39
#include "module-template.h"
40
#include "net.h"
41
#include "tcpsrv.h"
42
#include "tcps_sess.h"
43
#include "obj.h"
44
#include "errmsg.h"
1.2.4 by Michael Biebl
Import upstream version 3.20.4
45
#include "netstrm.h"
1.2.7 by Michael Biebl
Import upstream version 4.2.0
46
#include "msg.h"
1.1.2 by Michael Biebl
Import upstream version 3.14.2
47
48
49
/* static data */
50
DEFobjStaticHelpers
1.2.6 by Michael Biebl
Import upstream version 3.22.0
51
DEFobjCurrIf(glbl)
1.1.2 by Michael Biebl
Import upstream version 3.14.2
52
DEFobjCurrIf(errmsg)
1.2.4 by Michael Biebl
Import upstream version 3.20.4
53
DEFobjCurrIf(netstrm)
1.1.2 by Michael Biebl
Import upstream version 3.14.2
54
1.2.6 by Michael Biebl
Import upstream version 3.22.0
55
static int iMaxLine; /* maximum size of a single message */
56
1.1.2 by Michael Biebl
Import upstream version 3.14.2
57
/* forward definitions */
58
static rsRetVal Close(tcps_sess_t *pThis);
59
60
1.2.6 by Michael Biebl
Import upstream version 3.22.0
61
/* Standard-Constructor */
1.1.2 by Michael Biebl
Import upstream version 3.14.2
62
BEGINobjConstruct(tcps_sess) /* be sure to specify the object type also in END macro! */
63
		pThis->iMsg = 0; /* just make sure... */
64
		pThis->bAtStrtOfFram = 1; /* indicate frame header expected */
65
		pThis->eFraming = TCP_FRAMING_OCTET_STUFFING; /* just make sure... */
1.2.6 by Michael Biebl
Import upstream version 3.22.0
66
		/* now allocate the message reception buffer */
67
		CHKmalloc(pThis->pMsg = (uchar*) malloc(sizeof(uchar) * iMaxLine + 1));
68
finalize_it:
1.1.2 by Michael Biebl
Import upstream version 3.14.2
69
ENDobjConstruct(tcps_sess)
70
71
72
/* ConstructionFinalizer
73
 */
74
static rsRetVal
75
tcps_sessConstructFinalize(tcps_sess_t __attribute__((unused)) *pThis)
76
{
77
	DEFiRet;
78
	ISOBJ_TYPE_assert(pThis, tcps_sess);
79
	if(pThis->pSrv->OnSessConstructFinalize != NULL) {
80
		CHKiRet(pThis->pSrv->OnSessConstructFinalize(&pThis->pUsr));
81
	}
82
83
finalize_it:
84
	RETiRet;
85
}
86
87
88
/* destructor for the tcps_sess object */
89
BEGINobjDestruct(tcps_sess) /* be sure to specify the object type also in END and CODESTART macros! */
90
CODESTARTobjDestruct(tcps_sess)
1.2.4 by Michael Biebl
Import upstream version 3.20.4
91
	if(pThis->pStrm != NULL)
92
		netstrm.Destruct(&pThis->pStrm);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
93
94
	if(pThis->pSrv->pOnSessDestruct != NULL) {
95
		pThis->pSrv->pOnSessDestruct(&pThis->pUsr);
96
	}
97
	/* now destruct our own properties */
98
	if(pThis->fromHost != NULL)
99
		free(pThis->fromHost);
1.2.4 by Michael Biebl
Import upstream version 3.20.4
100
	if(pThis->fromHostIP != NULL)
101
		free(pThis->fromHostIP);
1.2.6 by Michael Biebl
Import upstream version 3.22.0
102
	if(pThis->pMsg != NULL)
103
		free(pThis->pMsg);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
104
ENDobjDestruct(tcps_sess)
105
106
107
/* debugprint for the tcps_sess object */
108
BEGINobjDebugPrint(tcps_sess) /* be sure to specify the object type also in END and CODESTART macros! */
109
CODESTARTobjDebugPrint(tcps_sess)
110
ENDobjDebugPrint(tcps_sess)
111
112
113
/* set property functions */
1.2.4 by Michael Biebl
Import upstream version 3.20.4
114
/* set the hostname. Note that the caller *hands over* the string. That is,
115
 * the caller no longer controls it once SetHost() has received it. Most importantly,
116
 * the caller must not free it. -- rgerhards, 2008-04-24
117
 */
1.1.2 by Michael Biebl
Import upstream version 3.14.2
118
static rsRetVal
119
SetHost(tcps_sess_t *pThis, uchar *pszHost)
120
{
121
	DEFiRet;
122
123
	ISOBJ_TYPE_assert(pThis, tcps_sess);
124
125
	if(pThis->fromHost != NULL) {
126
		free(pThis->fromHost);
1.2.4 by Michael Biebl
Import upstream version 3.20.4
127
	}
128
	
129
	pThis->fromHost = pszHost;
130
131
	RETiRet;
132
}
133
134
/* set the remote host's IP. Note that the caller *hands over* the string. That is,
135
 * the caller no longer controls it once SetHostIP() has received it. Most importantly,
136
 * the caller must not free it. -- rgerhards, 2008-05-16
137
 */
138
static rsRetVal
139
SetHostIP(tcps_sess_t *pThis, uchar *pszHostIP)
140
{
141
	DEFiRet;
142
143
	ISOBJ_TYPE_assert(pThis, tcps_sess);
144
145
	if(pThis->fromHostIP != NULL) {
146
		free(pThis->fromHostIP);
147
	}
148
	
149
	pThis->fromHostIP = pszHostIP;
150
151
	RETiRet;
152
}
153
154
static rsRetVal
155
SetStrm(tcps_sess_t *pThis, netstrm_t *pStrm)
156
{
157
	DEFiRet;
158
	ISOBJ_TYPE_assert(pThis, tcps_sess);
159
	pThis->pStrm = pStrm;
160
	RETiRet;
161
}
162
1.1.2 by Michael Biebl
Import upstream version 3.14.2
163
164
static rsRetVal
165
SetMsgIdx(tcps_sess_t *pThis, int idx)
166
{
167
	DEFiRet;
168
	ISOBJ_TYPE_assert(pThis, tcps_sess);
169
	pThis->iMsg = idx;
170
	RETiRet;
171
}
172
173
1.2.4 by Michael Biebl
Import upstream version 3.20.4
174
/* set our parent, the tcpsrv object */
1.1.2 by Michael Biebl
Import upstream version 3.14.2
175
static rsRetVal
176
SetTcpsrv(tcps_sess_t *pThis, tcpsrv_t *pSrv)
177
{
178
	DEFiRet;
179
	ISOBJ_TYPE_assert(pThis, tcps_sess);
180
	ISOBJ_TYPE_assert(pSrv, tcpsrv);
181
	pThis->pSrv = pSrv;
182
	RETiRet;
183
}
184
185
186
static rsRetVal
187
SetUsrP(tcps_sess_t *pThis, void *pUsr)
188
{
189
	DEFiRet;
190
	pThis->pUsr = pUsr;
191
	RETiRet;
192
}
193
194
195
/* This should be called before a normal (non forced) close
196
 * of a TCP session. This function checks if there is any unprocessed
197
 * message left in the TCP stream. Such a message is probably a
198
 * fragement. If evrything goes well, we must be right at the
199
 * beginnig of a new frame without any data received from it. If
200
 * not, there is some kind of a framing error. I think I remember that
201
 * some legacy syslog/TCP implementations have non-LF terminated
202
 * messages at the end of the stream. For now, we allow this behaviour.
203
 * Later, it should probably become a configuration option.
204
 * rgerhards, 2006-12-07
205
 */
206
static rsRetVal
207
PrepareClose(tcps_sess_t *pThis)
208
{
209
	DEFiRet;
210
211
	ISOBJ_TYPE_assert(pThis, tcps_sess);
212
	
213
	if(pThis->bAtStrtOfFram == 1) {
214
		/* this is how it should be. There is no unprocessed
215
		 * data left and such we have nothing to do. For simplicity
216
		 * reasons, we immediately return in that case.
217
		 */
218
		 FINALIZE;
219
	}
220
221
	/* we have some data left! */
222
	if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
223
		/* In this case, we have an invalid frame count and thus
224
		 * generate an error message and discard the frame.
225
		 */
1.2.4 by Michael Biebl
Import upstream version 3.20.4
226
		errmsg.LogError(0, NO_ERRCODE, "Incomplete frame at end of stream in session %p - "
227
			    "ignoring extra data (a message may be lost).\n", pThis->pStrm);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
228
		/* nothing more to do */
229
	} else { /* here, we have traditional framing. Missing LF at the end
230
		 * of message may occur. As such, we process the message in
231
		 * this case.
232
		 */
233
		dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n");
1.2.7 by Michael Biebl
Import upstream version 4.2.0
234
		parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
235
				      PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
236
		pThis->bAtStrtOfFram = 1;
237
	}
238
239
finalize_it:
240
	RETiRet;
241
}
242
243
244
/* Closes a TCP session
245
 * No attention is paid to the return code
246
 * of close, so potential-double closes are not detected.
247
 */
248
static rsRetVal
249
Close(tcps_sess_t *pThis)
250
{
251
	DEFiRet;
252
253
	ISOBJ_TYPE_assert(pThis, tcps_sess);
1.2.4 by Michael Biebl
Import upstream version 3.20.4
254
	netstrm.Destruct(&pThis->pStrm);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
255
	free(pThis->fromHost);
256
	pThis->fromHost = NULL; /* not really needed, but... */
1.2.4 by Michael Biebl
Import upstream version 3.20.4
257
	free(pThis->fromHostIP);
258
	pThis->fromHostIP = NULL; /* not really needed, but... */
1.1.2 by Michael Biebl
Import upstream version 3.14.2
259
260
	RETiRet;
261
}
262
263
264
/* process the data received. As TCP is stream based, we need to process the
265
 * data inside a state machine. The actual data received is passed in byte-by-byte
266
 * from DataRcvd, and this function here compiles messages from them and submits
267
 * the end result to the queue. Introducing this function fixes a long-term bug ;)
268
 * rgerhards, 2008-03-14
269
 */
270
static rsRetVal
271
processDataRcvd(tcps_sess_t *pThis, char c)
272
{
273
	DEFiRet;
274
	ISOBJ_TYPE_assert(pThis, tcps_sess);
275
276
	if(pThis->inputState == eAtStrtFram) {
277
		if(isdigit((int) c)) {
278
			pThis->inputState = eInOctetCnt;
279
			pThis->iOctetsRemain = 0;
280
			pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
281
		} else {
282
			pThis->inputState = eInMsg;
283
			pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
284
		}
285
	}
286
287
	if(pThis->inputState == eInOctetCnt) {
288
		if(isdigit(c)) {
289
			pThis->iOctetsRemain = pThis->iOctetsRemain * 10 + c - '0';
290
		} else { /* done with the octet count, so this must be the SP terminator */
291
			dbgprintf("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
292
			if(c != ' ') {
1.2.4 by Michael Biebl
Import upstream version 3.20.4
293
				errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
1.1.2 by Michael Biebl
Import upstream version 3.14.2
294
					    "delimiter is not SP but has ASCII value %d.\n", c);
295
			}
296
			if(pThis->iOctetsRemain < 1) {
297
				/* TODO: handle the case where the octet count is 0! */
298
				dbgprintf("Framing Error: invalid octet count\n");
1.2.4 by Michael Biebl
Import upstream version 3.20.4
299
				errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
1.1.2 by Michael Biebl
Import upstream version 3.14.2
300
					    "invalid octet count %d.\n", pThis->iOctetsRemain);
1.2.6 by Michael Biebl
Import upstream version 3.22.0
301
			} else if(pThis->iOctetsRemain > iMaxLine) {
1.1.2 by Michael Biebl
Import upstream version 3.14.2
302
				/* while we can not do anything against it, we can at least log an indication
303
				 * that something went wrong) -- rgerhards, 2008-03-14
304
				 */
1.2.6 by Michael Biebl
Import upstream version 3.22.0
305
				dbgprintf("truncating message with %d octets - max msg size is %d\n",
306
					  pThis->iOctetsRemain, iMaxLine);
1.2.4 by Michael Biebl
Import upstream version 3.20.4
307
				errmsg.LogError(0, NO_ERRCODE, "received oversize message: size is %d bytes, "
1.2.6 by Michael Biebl
Import upstream version 3.22.0
308
					        "max msg size is %d, truncating...\n", pThis->iOctetsRemain, iMaxLine);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
309
			}
310
			pThis->inputState = eInMsg;
311
		}
312
	} else {
313
		assert(pThis->inputState == eInMsg);
1.2.6 by Michael Biebl
Import upstream version 3.22.0
314
		if(pThis->iMsg >= iMaxLine) {
1.1.2 by Michael Biebl
Import upstream version 3.14.2
315
			/* emergency, we now need to flush, no matter if we are at end of message or not... */
1.2.6 by Michael Biebl
Import upstream version 3.22.0
316
			dbgprintf("error: message received is larger than max msg size, we split it\n");
317
			parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
1.2.7 by Michael Biebl
Import upstream version 4.2.0
318
				      	      PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
319
			pThis->iMsg = 0;
320
			/* we might think if it is better to ignore the rest of the
321
			 * message than to treat it as a new one. Maybe this is a good
322
			 * candidate for a configuration parameter...
323
			 * rgerhards, 2006-12-04
324
			 */
325
		}
326
1.2.7 by Michael Biebl
Import upstream version 4.2.0
327
		if((   (c == '\n')
328
		   || ((pThis->pSrv->addtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->addtlFrameDelim))
329
		   ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
1.2.6 by Michael Biebl
Import upstream version 3.22.0
330
			parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
1.2.7 by Michael Biebl
Import upstream version 4.2.0
331
				      	      PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
332
			pThis->iMsg = 0;
333
			pThis->inputState = eAtStrtFram;
334
		} else {
335
			/* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
1.2.6 by Michael Biebl
Import upstream version 3.22.0
336
			 * If we have a message that is larger than the max msg size, we truncate it. This is the best
1.1.2 by Michael Biebl
Import upstream version 3.14.2
337
			 * we can do in light of what the engine supports. -- rgerhards, 2008-03-14
338
			 */
1.2.6 by Michael Biebl
Import upstream version 3.22.0
339
			if(pThis->iMsg < iMaxLine) {
340
				*(pThis->pMsg + pThis->iMsg++) = c;
1.1.2 by Michael Biebl
Import upstream version 3.14.2
341
			}
342
		}
343
344
		if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
345
			/* do we need to find end-of-frame via octet counting? */
346
			pThis->iOctetsRemain--;
347
			if(pThis->iOctetsRemain < 1) {
348
				/* we have end of frame! */
1.2.6 by Michael Biebl
Import upstream version 3.22.0
349
				parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
1.2.7 by Michael Biebl
Import upstream version 4.2.0
350
				      	      PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
351
				pThis->iMsg = 0;
352
				pThis->inputState = eAtStrtFram;
353
			}
354
		}
355
	}
356
357
	RETiRet;
358
}
359
360
361
/* Processes the data received via a TCP session. If there
362
 * is no other way to handle it, data is discarded.
363
 * Input parameter data is the data received, iLen is its
364
 * len as returned from recv(). iLen must be 1 or more (that
365
 * is errors must be handled by caller!). iTCPSess must be
366
 * the index of the TCP session that received the data.
367
 * rgerhards 2005-07-04
368
 * And another change while generalizing. We now return either
369
 * RS_RET_OK, which means the session should be kept open
370
 * or anything else, which means it must be closed.
371
 * rgerhards, 2008-03-01
372
 */
373
static rsRetVal
374
DataRcvd(tcps_sess_t *pThis, char *pData, size_t iLen)
375
{
376
	DEFiRet;
377
	char *pEnd;
378
379
	ISOBJ_TYPE_assert(pThis, tcps_sess);
380
	assert(pData != NULL);
381
	assert(iLen > 0);
382
1.2.4 by Michael Biebl
Import upstream version 3.20.4
383
	 /* We now copy the message to the session buffer. */
1.1.2 by Michael Biebl
Import upstream version 3.14.2
384
	pEnd = pData + iLen; /* this is one off, which is intensional */
385
386
	while(pData < pEnd) {
387
		CHKiRet(processDataRcvd(pThis, *pData++));
388
	}
389
390
finalize_it:
391
	RETiRet;
392
}
393
394
395
/* queryInterface function
396
 * rgerhards, 2008-02-29
397
 */
398
BEGINobjQueryInterface(tcps_sess)
399
CODESTARTobjQueryInterface(tcps_sess)
400
	if(pIf->ifVersion != tcps_sessCURR_IF_VERSION) { /* check for current version, increment on each change */
401
		ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
402
	}
403
404
	/* ok, we have the right interface, so let's fill it
405
	 * Please note that we may also do some backwards-compatibility
406
	 * work here (if we can support an older interface version - that,
407
	 * of course, also affects the "if" above).
408
	 */
409
	pIf->DebugPrint = tcps_sessDebugPrint;
410
	pIf->Construct = tcps_sessConstruct;
411
	pIf->ConstructFinalize = tcps_sessConstructFinalize;
412
	pIf->Destruct = tcps_sessDestruct;
413
414
	pIf->PrepareClose = PrepareClose;
415
	pIf->Close = Close;
416
	pIf->DataRcvd = DataRcvd;
417
418
	pIf->SetUsrP = SetUsrP;
419
	pIf->SetTcpsrv = SetTcpsrv;
420
	pIf->SetHost = SetHost;
1.2.4 by Michael Biebl
Import upstream version 3.20.4
421
	pIf->SetHostIP = SetHostIP;
422
	pIf->SetStrm = SetStrm;
1.1.2 by Michael Biebl
Import upstream version 3.14.2
423
	pIf->SetMsgIdx = SetMsgIdx;
424
finalize_it:
425
ENDobjQueryInterface(tcps_sess)
426
427
428
/* exit our class
429
 * rgerhards, 2008-03-10
430
 */
431
BEGINObjClassExit(tcps_sess, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
432
CODESTARTObjClassExit(tcps_sess)
433
	/* release objects we no longer need */
434
	objRelease(errmsg, CORE_COMPONENT);
1.2.4 by Michael Biebl
Import upstream version 3.20.4
435
	objRelease(netstrm, LM_NETSTRMS_FILENAME);
1.1.2 by Michael Biebl
Import upstream version 3.14.2
436
ENDObjClassExit(tcps_sess)
437
438
439
/* Initialize our class. Must be called as the very first method
440
 * before anything else is called inside this class.
441
 * rgerhards, 2008-02-29
442
 */
443
BEGINObjClassInit(tcps_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
444
	/* request objects we use */
445
	CHKiRet(objUse(errmsg, CORE_COMPONENT));
1.2.4 by Michael Biebl
Import upstream version 3.20.4
446
	CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
1.1.2 by Michael Biebl
Import upstream version 3.14.2
447
1.2.6 by Michael Biebl
Import upstream version 3.22.0
448
	CHKiRet(objUse(glbl, CORE_COMPONENT));
449
	iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
450
	objRelease(glbl, CORE_COMPONENT);
451
1.1.2 by Michael Biebl
Import upstream version 3.14.2
452
	/* set our own handlers */
453
	OBJSetMethodHandler(objMethod_DEBUGPRINT, tcps_sessDebugPrint);
454
	OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, tcps_sessConstructFinalize);
455
ENDObjClassInit(tcps_sess)
456
457
458
459
/* vim:set ai:
460
 */