rsyslog/runtime/netstrm.c
Rainer Gerhards a900a7c34b greatly enhanced testbench
The imdiag module now can very effectively inject messages, which also
frees us from uncertainties of tcp reception and processing. All shell
script based tests have been modularized, what makes it far easier to
create new tests. Also, the test bench now executes more reliable and
much faster, because we can now rely on actual engine information where
we previously did just a dumb sleep.
2009-05-27 11:29:47 +02:00

366 lines
10 KiB
C

/* netstrm.c
*
* This class implements a generic netstrmwork stream class. It supports
* sending and receiving data streams over a netstrmwork. The class abstracts
* the transport, though it is a safe assumption that TCP is being used.
* The class has a number of properties, among which are also ones to
* select privacy settings, eg by enabling TLS and/or GSSAPI. In the
* long run, this class shall provide all stream-oriented netstrmwork
* functionality inside rsyslog.
*
* It is a high-level class, which uses a number of helper objects
* to carry out its work (including, and most importantly, transport
* drivers).
*
* Work on this module begun 2008-04-17 by Rainer Gerhards. This code
* borrows from librelp's tcp.c/.h code. librelp is dual licensed and
* Rainer Gerhards and Adiscon GmbH have agreed to permit using the code
* under the terms of the GNU Lesser General Public License.
*
* Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
* The rsyslog runtime library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The rsyslog runtime library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
* A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
*/
#include "config.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "rsyslog.h"
#include "net.h"
#include "module-template.h"
#include "obj.h"
#include "errmsg.h"
#include "netstrms.h"
#include "netstrm.h"
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
DEFobjCurrIf(netstrms)
/* Standard-Constructor */
BEGINobjConstruct(netstrm) /* be sure to specify the object type also in END macro! */
ENDobjConstruct(netstrm)
/* destructor for the netstrm object */
BEGINobjDestruct(netstrm) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(netstrm)
if(pThis->pDrvrData != NULL)
iRet = pThis->Drvr.Destruct(&pThis->pDrvrData);
ENDobjDestruct(netstrm)
/* ConstructionFinalizer */
static rsRetVal
netstrmConstructFinalize(netstrm_t *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
CHKiRet(pThis->Drvr.Construct(&pThis->pDrvrData));
finalize_it:
RETiRet;
}
/* abort a connection. This is much like Destruct(), but tries
* to discard any unsent data. -- rgerhards, 2008-03-24
*/
static rsRetVal
AbortDestruct(netstrm_t **ppThis)
{
DEFiRet;
assert(ppThis != NULL);
ISOBJ_TYPE_assert((*ppThis), netstrm);
/* we do NOT exit on error, because that would make things worse */
(*ppThis)->Drvr.Abort((*ppThis)->pDrvrData);
iRet = netstrmDestruct(ppThis);
RETiRet;
}
/* accept an incoming connection request
* The netstrm instance that had the incoming request must be provided. If
* the connection request succeeds, a new netstrm object is created and
* passed back to the caller. The caller is responsible for destructing it.
* pReq is the nsd_t obj that has the accept request.
* rgerhards, 2008-04-21
*/
static rsRetVal
AcceptConnReq(netstrm_t *pThis, netstrm_t **ppNew)
{
nsd_t *pNewNsd = NULL;
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
assert(ppNew != NULL);
/* accept the new connection */
CHKiRet(pThis->Drvr.AcceptConnReq(pThis->pDrvrData, &pNewNsd));
/* construct our object so that we can use it... */
CHKiRet(objUse(netstrms, DONT_LOAD_LIB)); /* use netstrms obj if not already done so */
CHKiRet(netstrms.CreateStrm(pThis->pNS, ppNew));
(*ppNew)->pDrvrData = pNewNsd;
finalize_it:
if(iRet != RS_RET_OK) {
/* the close may be redundant, but that doesn't hurt... */
if(pNewNsd != NULL)
pThis->Drvr.Destruct(&pNewNsd);
}
RETiRet;
}
/* make the netstrm listen to specified port and IP.
* pLstnIP points to the port to listen to (NULL means "all"),
* iMaxSess has the maximum number of sessions permitted (this ist just a hint).
* pLstnPort must point to a port name or number. NULL is NOT permitted.
* rgerhards, 2008-04-22
*/
static rsRetVal
LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*),
uchar *pLstnPort, uchar *pLstnIP, int iSessMax)
{
DEFiRet;
ISOBJ_TYPE_assert(pNS, netstrms);
assert(fAddLstn != NULL);
assert(pLstnPort != NULL);
CHKiRet(pNS->Drvr.LstnInit(pNS, pUsr, fAddLstn, pLstnPort, pLstnIP, iSessMax));
finalize_it:
RETiRet;
}
/* receive data from a tcp socket
* The lenBuf parameter must contain the max buffer size on entry and contains
* the number of octets read (or -1 in case of error) on exit. This function
* never blocks, not even when called on a blocking socket. That is important
* for client sockets, which are set to block during send, but should not
* block when trying to read data. If *pLenBuf is -1, an error occured and
* errno holds the exact error cause.
* rgerhards, 2008-03-17
*/
static rsRetVal
Rcv(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.Rcv(pThis->pDrvrData, pBuf, pLenBuf);
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.
* -------------------------------------------------------------------------- */
/* set the driver mode
* rgerhards, 2008-04-28
*/
static rsRetVal
SetDrvrMode(netstrm_t *pThis, int iMode)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.SetMode(pThis->pDrvrData, iMode);
RETiRet;
}
/* set the driver authentication mode -- rgerhards, 2008-05-16
*/
static rsRetVal
SetDrvrAuthMode(netstrm_t *pThis, uchar *mode)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.SetAuthMode(pThis->pDrvrData, mode);
RETiRet;
}
/* set the driver's permitted peers -- rgerhards, 2008-05-19 */
static rsRetVal
SetDrvrPermPeers(netstrm_t *pThis, permittedPeers_t *pPermPeers)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.SetPermPeers(pThis->pDrvrData, pPermPeers);
RETiRet;
}
/* End of methods to shuffle autentication settings to the driver.
* -------------------------------------------------------------------------- */
/* send a buffer. On entry, pLenBuf contains the number of octets to
* write. On exit, it contains the number of octets actually written.
* If this number is lower than on entry, only a partial buffer has
* been written.
* rgerhards, 2008-03-19
*/
static rsRetVal
Send(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.Send(pThis->pDrvrData, pBuf, pLenBuf);
RETiRet;
}
/* check connection - slim wrapper for NSD driver function */
static void
CheckConnection(netstrm_t *pThis)
{
ISOBJ_TYPE_assert(pThis, netstrm);
pThis->Drvr.CheckConnection(pThis->pDrvrData);
}
/* get remote hname - slim wrapper for NSD driver function */
static rsRetVal
GetRemoteHName(netstrm_t *pThis, uchar **ppsz)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.GetRemoteHName(pThis->pDrvrData, ppsz);
RETiRet;
}
/* get remote IP - slim wrapper for NSD driver function */
static rsRetVal
GetRemoteIP(netstrm_t *pThis, uchar **ppsz)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.GetRemoteIP(pThis->pDrvrData, ppsz);
RETiRet;
}
/* get remote addr - slim wrapper for NSD driver function */
static rsRetVal
GetRemAddr(netstrm_t *pThis, struct sockaddr_storage **ppAddr)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
iRet = pThis->Drvr.GetRemAddr(pThis->pDrvrData, ppAddr);
RETiRet;
}
/* open a connection to a remote host (server).
* rgerhards, 2008-03-19
*/
static rsRetVal
Connect(netstrm_t *pThis, int family, uchar *port, uchar *host)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
assert(port != NULL);
assert(host != NULL);
iRet = pThis->Drvr.Connect(pThis->pDrvrData, family, port, host);
RETiRet;
}
/* Provide access to the underlying OS socket. This is dirty
* and scheduled to be removed. Does not work with all nsd drivers.
* See comment in netstrm interface for details.
* rgerhards, 2008-05-05
*/
static rsRetVal
GetSock(netstrm_t *pThis, int *pSock)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, netstrm);
assert(pSock != NULL);
iRet = pThis->Drvr.GetSock(pThis->pDrvrData, pSock);
RETiRet;
}
/* queryInterface function
*/
BEGINobjQueryInterface(netstrm)
CODESTARTobjQueryInterface(netstrm)
if(pIf->ifVersion != netstrmCURR_IF_VERSION) {/* check for current version, increment on each change */
ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
}
/* ok, we have the right interface, so let's fill it
* Please note that we may also do some backwards-compatibility
* work here (if we can support an older interface version - that,
* of course, also affects the "if" above).
*/
pIf->Construct = netstrmConstruct;
pIf->ConstructFinalize = netstrmConstructFinalize;
pIf->Destruct = netstrmDestruct;
pIf->AbortDestruct = AbortDestruct;
pIf->Rcv = Rcv;
pIf->Send = Send;
pIf->Connect = Connect;
pIf->LstnInit = LstnInit;
pIf->AcceptConnReq = AcceptConnReq;
pIf->GetRemoteHName = GetRemoteHName;
pIf->GetRemoteIP = GetRemoteIP;
pIf->GetRemAddr = GetRemAddr;
pIf->SetDrvrMode = SetDrvrMode;
pIf->SetDrvrAuthMode = SetDrvrAuthMode;
pIf->SetDrvrPermPeers = SetDrvrPermPeers;
pIf->CheckConnection = CheckConnection;
pIf->GetSock = GetSock;
finalize_it:
ENDobjQueryInterface(netstrm)
/* exit our class
*/
BEGINObjClassExit(netstrm, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
CODESTARTObjClassExit(netstrm)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
objRelease(netstrms, DONT_LOAD_LIB);
ENDObjClassExit(netstrm)
/* Initialize the netstrm class. Must be called as the very first method
* before anything else is called inside this class.
* rgerhards, 2008-02-19
*/
BEGINAbstractObjClassInit(netstrm, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
/* set our own handlers */
ENDObjClassInit(netstrm)
/* vi:set ai:
*/