/* 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 . * * 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 #include #include #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) //printf("destruct driver data %p\n", pThis->pDrvrData); 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); //printf("Rcv %p\n", pThis); 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; } /* Enable Keep-Alive handling for those drivers that support it. * rgerhards, 2009-06-02 */ static rsRetVal EnableKeepAlive(netstrm_t *pThis) { DEFiRet; ISOBJ_TYPE_assert(pThis, netstrm); iRet = pThis->Drvr.EnableKeepAlive(pThis->pDrvrData); 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; pIf->EnableKeepAlive = EnableKeepAlive; 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: */