summaryrefslogtreecommitdiffstats
path: root/tcpsrv.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcpsrv.c')
-rw-r--r--tcpsrv.c814
1 files changed, 814 insertions, 0 deletions
diff --git a/tcpsrv.c b/tcpsrv.c
new file mode 100644
index 00000000..85b34947
--- /dev/null
+++ b/tcpsrv.c
@@ -0,0 +1,814 @@
+/* tcpsrv.c
+ *
+ * Common code for plain TCP based servers. This is currently being
+ * utilized by imtcp and imgssapi. I suspect that when we implement
+ * SSL/TLS, that module could also use tcpsrv.
+ *
+ * There are actually two classes within the tcpserver code: one is
+ * the tcpsrv itself, the other one is its sessions. This is a helper
+ * class to tcpsrv.
+ *
+ * The common code here calls upon specific functionality by using
+ * callbacks. The specialised input modules need to set the proper
+ * callbacks before the code is run. The tcpsrv then calls back
+ * into the specific input modules at the appropriate time.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "dirty.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "net.h"
+#include "srUtils.h"
+#include "conf.h"
+#include "tcpsrv.h"
+#include "obj.h"
+#include "glbl.h"
+#include "netstrms.h"
+#include "netstrm.h"
+#include "nssel.h"
+#include "errmsg.h"
+
+MODULE_TYPE_LIB
+
+/* defines */
+#define TCPSESS_MAX_DEFAULT 200 /* default for nbr of tcp sessions if no number is given */
+#define TCPLSTN_MAX_DEFAULT 20 /* default for nbr of listeners */
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(conf)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(tcps_sess)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(net)
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(nssel)
+
+
+/* configure TCP listener settings. This is called during command
+ * line parsing. The argument following -t is supplied as an argument.
+ * The format of this argument is
+ * "<port-to-use>, <nbr-of-sessions>"
+ * Typically, there is no whitespace between port and session number.
+ * (but it may be...).
+ * NOTE: you can not use dbgprintf() in here - the dbgprintf() system is
+ * not yet initilized when this function is called.
+ * rgerhards, 2007-06-21
+ * The port in cOptarg is handed over to us - the caller MUST NOT free it!
+ * rgerhards, 2008-03-20
+ */
+static void
+configureTCPListen(tcpsrv_t *pThis, char *cOptarg)
+{
+ register int i;
+ register char *pArg = cOptarg;
+
+ assert(cOptarg != NULL);
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* extract port */
+ i = 0;
+ while(isdigit((int) *pArg)) {
+ i = i * 10 + *pArg++ - '0';
+ }
+
+ if(pThis->TCPLstnPort != NULL) {
+ free(pThis->TCPLstnPort);
+ pThis->TCPLstnPort = NULL;
+ }
+
+ if( i >= 0 && i <= 65535) {
+ pThis->TCPLstnPort = cOptarg;
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg);
+ }
+}
+
+
+/* Initialize the session table
+ * returns 0 if OK, somewhat else otherwise
+ */
+static rsRetVal
+TCPSessTblInit(tcpsrv_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ assert(pThis->pSessions == NULL);
+
+ dbgprintf("Allocating buffer for %d TCP sessions.\n", pThis->iSessMax);
+ if((pThis->pSessions = (tcps_sess_t **) calloc(pThis->iSessMax, sizeof(tcps_sess_t *))) == NULL) {
+ dbgprintf("Error: TCPSessInit() could not alloc memory for TCP session table.\n");
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find a free spot in the session table. If the table
+ * is full, -1 is returned, else the index of the free
+ * entry (0 or higher).
+ */
+static int
+TCPSessTblFindFreeSpot(tcpsrv_t *pThis)
+{
+ register int i;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ for(i = 0 ; i < pThis->iSessMax ; ++i) {
+ if(pThis->pSessions[i] == NULL)
+ break;
+ }
+
+ return((i < pThis->iSessMax) ? i : -1);
+}
+
+
+/* Get the next session index. Free session tables entries are
+ * skipped. This function is provided the index of the last
+ * session entry, or -1 if no previous entry was obtained. It
+ * returns the index of the next session or -1, if there is no
+ * further entry in the table. Please note that the initial call
+ * might as well return -1, if there is no session at all in the
+ * session table.
+ */
+static int
+TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
+{
+ register int i;
+
+ BEGINfunc
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ assert(pThis->pSessions != NULL);
+ for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) {
+ if(pThis->pSessions[i] != NULL)
+ break;
+ }
+
+ ENDfunc
+ return((i < pThis->iSessMax) ? i : -1);
+}
+
+
+/* De-Initialize TCP listner sockets.
+ * This function deinitializes everything, including freeing the
+ * session table. No TCP listen receive operations are permitted
+ * unless the subsystem is reinitialized.
+ * rgerhards, 2007-06-21
+ */
+static void deinit_tcp_listener(tcpsrv_t *pThis)
+{
+ int i;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ if(pThis->pSessions != NULL) {
+ /* close all TCP connections! */
+ i = TCPSessGetNxtSess(pThis, -1);
+ while(i != -1) {
+ tcps_sess.Destruct(&pThis->pSessions[i]);
+ /* now get next... */
+ i = TCPSessGetNxtSess(pThis, i);
+ }
+
+ /* we are done with the session table - so get rid of it... */
+ free(pThis->pSessions);
+ pThis->pSessions = NULL; /* just to make sure... */
+ }
+
+ if(pThis->TCPLstnPort != NULL)
+ free(pThis->TCPLstnPort);
+
+ /* finally close our listen streams */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ netstrm.Destruct(pThis->ppLstn + i);
+ }
+}
+
+
+/* add a listen socket to our listen socket array. This is a callback
+ * invoked from the netstrm class. -- rgerhards, 2008-04-23
+ */
+static rsRetVal
+addTcpLstn(void *pUsr, netstrm_t *pLstn)
+{
+ tcpsrv_t *pThis = (tcpsrv_t*) pUsr;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ ISOBJ_TYPE_assert(pLstn, netstrm);
+
+ if(pThis->iLstnMax >= TCPLSTN_MAX_DEFAULT)
+ ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
+
+ pThis->ppLstn[pThis->iLstnMax] = pLstn;
+ ++pThis->iLstnMax;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize TCP sockets (for listener) and listens on them */
+static rsRetVal
+create_tcp_socket(tcpsrv_t *pThis)
+{
+ DEFiRet;
+ uchar *TCPLstnPort;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ if(!strcmp((char*)pThis->TCPLstnPort, "0"))
+ TCPLstnPort = (uchar*)"514";
+ // TODO: we need to enable the caller to set a port (based on who is
+ // using this, 514 may be totally unsuitable... --- rgerhards, 2008-04-22
+ /* use default - we can not do service db update, because there is
+ * no IANA-assignment for syslog/tcp. In the long term, we might
+ * re-use RFC 3195 port of 601, but that would probably break to
+ * many existing configurations.
+ * rgerhards, 2007-06-28
+ */
+ else
+ TCPLstnPort = (uchar*)pThis->TCPLstnPort;
+
+ /* TODO: add capability to specify local listen address! */
+ CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pThis, addTcpLstn, TCPLstnPort, NULL, pThis->iSessMax));
+
+ /* OK, we had success. Now it is also time to
+ * initialize our connections
+ */
+ if(TCPSessTblInit(pThis) != 0) {
+ /* OK, we are in some trouble - we could not initialize the
+ * session table, so we can not continue. We need to free all
+ * we have assigned so far, because we can not really use it...
+ */
+ errmsg.LogError(0, RS_RET_ERR, "Could not initialize TCP session table, suspending TCP message reception.");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Accept new TCP connection; make entry in session table. If there
+ * is no more space left in the connection table, the new TCP
+ * connection is immediately dropped.
+ * ppSess has a pointer to the newly created session, if it succeds.
+ * If it does not succeed, no session is created and ppSess is
+ * undefined. If the user has provided an OnSessAccept Callback,
+ * this one is executed immediately after creation of the
+ * session object, so that it can do its own initialization.
+ * rgerhards, 2008-03-02
+ */
+static rsRetVal
+SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
+{
+ DEFiRet;
+ tcps_sess_t *pSess = NULL;
+ netstrm_t *pNewStrm = NULL;
+ int iSess = -1;
+ struct sockaddr_storage *addr;
+ uchar *fromHostFQDN = NULL;
+ uchar *fromHostIP = NULL;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ CHKiRet(netstrm.AcceptConnReq(pStrm, &pNewStrm));
+
+ /* Add to session list */
+ iSess = TCPSessTblFindFreeSpot(pThis);
+ if(iSess == -1) {
+ errno = 0;
+ errmsg.LogError(0, RS_RET_MAX_SESS_REACHED, "too many tcp sessions - dropping incoming request");
+ ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED);
+ } else {
+ /* we found a free spot and can construct our session object */
+ CHKiRet(tcps_sess.Construct(&pSess));
+ CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
+ }
+
+ /* OK, we have a "good" index... */
+ /* get the host name */
+ CHKiRet(netstrm.GetRemoteHName(pNewStrm, &fromHostFQDN));
+ CHKiRet(netstrm.GetRemoteIP(pNewStrm, &fromHostIP));
+ CHKiRet(netstrm.GetRemAddr(pNewStrm, &addr));
+ /* TODO: check if we need to strip the domain name here -- rgerhards, 2008-04-24 */
+
+ /* Here we check if a host is permitted to send us messages. If it isn't, we do not further
+ * process the message but log a warning (if we are configured to do this).
+ * rgerhards, 2005-09-26
+ */
+ if(!pThis->pIsPermittedHost((struct sockaddr*) addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) {
+ dbgprintf("%s is not an allowed sender\n", fromHostFQDN);
+ if(glbl.GetOption_DisallowWarning()) {
+ errno = 0;
+ errmsg.LogError(0, RS_RET_HOST_NOT_PERMITTED, "TCP message from disallowed sender %s discarded", fromHostFQDN);
+ }
+ ABORT_FINALIZE(RS_RET_HOST_NOT_PERMITTED);
+ }
+
+ /* OK, we have an allowed sender, so let's continue, what
+ * means we can finally fill in the session object.
+ */
+ CHKiRet(tcps_sess.SetHost(pSess, fromHostFQDN));
+ fromHostFQDN = NULL; /* we handed this string over */
+ CHKiRet(tcps_sess.SetHostIP(pSess, fromHostIP));
+ fromHostIP = NULL; /* we handed this string over */
+ CHKiRet(tcps_sess.SetStrm(pSess, pNewStrm));
+ pNewStrm = NULL; /* prevent it from being freed in error handler, now done in tcps_sess! */
+ CHKiRet(tcps_sess.SetMsgIdx(pSess, 0));
+ CHKiRet(tcps_sess.ConstructFinalize(pSess));
+
+ /* check if we need to call our callback */
+ if(pThis->pOnSessAccept != NULL) {
+ CHKiRet(pThis->pOnSessAccept(pThis, pSess));
+ }
+
+ *ppSess = pSess;
+ pThis->pSessions[iSess] = pSess;
+ pSess = NULL; /* this is now also handed over */
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pSess != NULL)
+ tcps_sess.Destruct(&pSess);
+ if(fromHostFQDN != NULL)
+ free(fromHostFQDN);
+ if(fromHostIP != NULL)
+ free(fromHostIP);
+ if(pNewStrm != NULL)
+ netstrm.Destruct(&pNewStrm);
+ }
+
+ RETiRet;
+}
+
+
+static void
+RunCancelCleanup(void *arg)
+{
+ nssel_t **ppSel = (nssel_t**) arg;
+
+ if(*ppSel != NULL)
+ nssel.Destruct(ppSel);
+}
+
+
+/* This function is called to gather input. */
+#pragma GCC diagnostic ignored "-Wempty-body"
+static rsRetVal
+Run(tcpsrv_t *pThis)
+{
+ DEFiRet;
+ int nfds;
+ int i;
+ int iTCPSess;
+ int bIsReady;
+ tcps_sess_t *pNewSess;
+ nssel_t *pSel;
+ ssize_t iRcvd;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* this is an endless loop - it is terminated by the framework canelling
+ * this thread. Thus, we also need to instantiate a cancel cleanup handler
+ * to prevent us from leaking anything. -- rgerharsd, 20080-04-24
+ */
+ pthread_cleanup_push(RunCancelCleanup, (void*) &pSel);
+ while(1) {
+ CHKiRet(nssel.Construct(&pSel));
+ // TODO: set driver
+ CHKiRet(nssel.ConstructFinalize(pSel));
+
+ /* Add the TCP listen sockets to the list of read descriptors. */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ CHKiRet(nssel.Add(pSel, pThis->ppLstn[i], NSDSEL_RD));
+ }
+
+ /* do the sessions */
+ iTCPSess = TCPSessGetNxtSess(pThis, -1);
+ while(iTCPSess != -1) {
+ /* TODO: access to pNsd is NOT really CLEAN, use method... */
+ CHKiRet(nssel.Add(pSel, pThis->pSessions[iTCPSess]->pStrm, NSDSEL_RD));
+ /* now get next... */
+ iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
+ }
+
+ /* wait for io to become ready */
+ CHKiRet(nssel.Wait(pSel, &nfds));
+
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ CHKiRet(nssel.IsReady(pSel, pThis->ppLstn[i], NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
+ dbgprintf("New connect on NSD %p.\n", pThis->ppLstn[i]);
+ SessAccept(pThis, &pNewSess, pThis->ppLstn[i]);
+ --nfds; /* indicate we have processed one */
+ }
+ }
+
+ /* now check the sessions */
+ iTCPSess = TCPSessGetNxtSess(pThis, -1);
+ while(nfds && iTCPSess != -1) {
+ CHKiRet(nssel.IsReady(pSel, pThis->pSessions[iTCPSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
+ char buf[MAXLINE];
+ dbgprintf("netstream %p with new data\n", pThis->pSessions[iTCPSess]->pStrm);
+
+ /* Receive message */
+ iRet = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf), &iRcvd);
+ switch(iRet) {
+ case RS_RET_CLOSED:
+ pThis->pOnRegularClose(pThis->pSessions[iTCPSess]);
+ tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
+ break;
+ case RS_RET_RETRY:
+ /* we simply ignore retry - this is not an error, but we also have not received anything */
+ break;
+ case RS_RET_OK:
+ /* valid data received, process it! */
+ if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, iRcvd) != RS_RET_OK) {
+ /* in this case, something went awfully wrong.
+ * We are instructed to terminate the session.
+ */
+ errmsg.LogError(0, NO_ERRCODE, "Tearing down TCP Session %d - see "
+ "previous messages for reason(s)\n", iTCPSess);
+ pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
+ tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
+ }
+ break;
+ default:
+ errno = 0;
+ errmsg.LogError(0, iRet, "netstream session %p will be closed due to error\n",
+ pThis->pSessions[iTCPSess]->pStrm);
+ pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
+ tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
+ break;
+ }
+ --nfds; /* indicate we have processed one */
+ }
+ iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
+ }
+ CHKiRet(nssel.Destruct(&pSel));
+finalize_it: /* this is a very special case - this time only we do not exit the function,
+ * because that would not help us either. So we simply retry it. Let's see
+ * if that actually is a better idea. Exiting the loop wasn't we always
+ * crashed, which made sense (the rest of the engine was not prepared for
+ * that) -- rgerhards, 2008-05-19
+ */
+ /*EMPTY*/;
+ }
+
+ /* note that this point is usually not reached */
+ pthread_cleanup_pop(0); /* remove cleanup handler */
+
+ RETiRet;
+}
+#pragma GCC diagnostic warning "-Wempty-body"
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
+ pThis->iSessMax = TCPSESS_MAX_DEFAULT; /* TODO: useful default ;) */
+ENDobjConstruct(tcpsrv)
+
+
+/* ConstructionFinalizer */
+static rsRetVal
+tcpsrvConstructFinalize(tcpsrv_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* prepare network stream subsystem */
+ CHKiRet(netstrms.Construct(&pThis->pNS));
+ CHKiRet(netstrms.SetDrvrMode(pThis->pNS, pThis->iDrvrMode));
+ if(pThis->pszDrvrAuthMode != NULL)
+ CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode));
+ if(pThis->pPermPeers != NULL)
+ CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers));
+ // TODO: set driver!
+ CHKiRet(netstrms.ConstructFinalize(pThis->pNS));
+
+ /* set up listeners */
+ CHKmalloc(pThis->ppLstn = calloc(TCPLSTN_MAX_DEFAULT, sizeof(netstrm_t*)));
+ iRet = pThis->OpenLstnSocks(pThis);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ }
+ RETiRet;
+}
+
+
+/* destructor for the tcpsrv object */
+BEGINobjDestruct(tcpsrv) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(tcpsrv)
+ if(pThis->OnDestruct != NULL)
+ pThis->OnDestruct(pThis->pUsr);
+
+ deinit_tcp_listener(pThis);
+
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ if(pThis->pszDrvrAuthMode != NULL)
+ free(pThis->pszDrvrAuthMode);
+ if(pThis->ppLstn != NULL)
+ free(pThis->ppLstn);
+ENDobjDestruct(tcpsrv)
+
+
+/* debugprint for the tcpsrv object */
+BEGINobjDebugPrint(tcpsrv) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(tcpsrv)
+ENDobjDebugPrint(tcpsrv)
+
+/* set functions */
+static rsRetVal
+SetCBIsPermittedHost(tcpsrv_t *pThis, int (*pCB)(struct sockaddr *addr, char *fromHostFQDN, void*, void*))
+{
+ DEFiRet;
+ pThis->pIsPermittedHost = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBRcvData(tcpsrv_t *pThis, rsRetVal (*pRcvData)(tcps_sess_t*, char*, size_t, ssize_t*))
+{
+ DEFiRet;
+ pThis->pRcvData = pRcvData;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnListenDeinit(tcpsrv_t *pThis, int (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->pOnListenDeinit = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessAccept(tcpsrv_t *pThis, rsRetVal (*pCB)(tcpsrv_t*, tcps_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnSessAccept = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnDestruct(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->OnDestruct = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessConstructFinalize(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->OnSessConstructFinalize = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessDestruct(tcpsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->pOnSessDestruct = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnRegularClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnRegularClose = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnErrClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnErrClose = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOpenLstnSocks(tcpsrv_t *pThis, rsRetVal (*pCB)(tcpsrv_t*))
+{
+ DEFiRet;
+ pThis->OpenLstnSocks = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetUsrP(tcpsrv_t *pThis, void *pUsr)
+{
+ DEFiRet;
+ pThis->pUsr = pUsr;
+ 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-30 */
+static rsRetVal
+SetDrvrMode(tcpsrv_t *pThis, int iMode)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ pThis->iDrvrMode = iMode;
+ RETiRet;
+}
+
+
+/* set the driver authentication mode -- rgerhards, 2008-05-19 */
+static rsRetVal
+SetDrvrAuthMode(tcpsrv_t *pThis, uchar *mode)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ CHKmalloc(pThis->pszDrvrAuthMode = (uchar*)strdup((char*)mode));
+finalize_it:
+ RETiRet;
+}
+
+
+/* set the driver's permitted peers -- rgerhards, 2008-05-19 */
+static rsRetVal
+SetDrvrPermPeers(tcpsrv_t *pThis, permittedPeers_t *pPermPeers)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ pThis->pPermPeers = pPermPeers;
+ RETiRet;
+}
+
+
+/* End of methods to shuffle autentication settings to the driver.;
+
+ * -------------------------------------------------------------------------- */
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-29
+ */
+BEGINobjQueryInterface(tcpsrv)
+CODESTARTobjQueryInterface(tcpsrv)
+ if(pIf->ifVersion != tcpsrvCURR_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->DebugPrint = tcpsrvDebugPrint;
+ pIf->Construct = tcpsrvConstruct;
+ pIf->ConstructFinalize = tcpsrvConstructFinalize;
+ pIf->Destruct = tcpsrvDestruct;
+
+ pIf->SessAccept = SessAccept;
+ pIf->configureTCPListen = configureTCPListen;
+ pIf->create_tcp_socket = create_tcp_socket;
+ pIf->Run = Run;
+
+ pIf->SetUsrP = SetUsrP;
+ pIf->SetDrvrMode = SetDrvrMode;
+ pIf->SetDrvrAuthMode = SetDrvrAuthMode;
+ pIf->SetDrvrPermPeers = SetDrvrPermPeers;
+ pIf->SetCBIsPermittedHost = SetCBIsPermittedHost;
+ pIf->SetCBOpenLstnSocks = SetCBOpenLstnSocks;
+ pIf->SetCBRcvData = SetCBRcvData;
+ pIf->SetCBOnListenDeinit = SetCBOnListenDeinit;
+ pIf->SetCBOnSessAccept = SetCBOnSessAccept;
+ pIf->SetCBOnSessConstructFinalize = SetCBOnSessConstructFinalize;
+ pIf->SetCBOnSessDestruct = SetCBOnSessDestruct;
+ pIf->SetCBOnDestruct = SetCBOnDestruct;
+ pIf->SetCBOnRegularClose = SetCBOnRegularClose;
+ pIf->SetCBOnErrClose = SetCBOnErrClose;
+
+finalize_it:
+ENDobjQueryInterface(tcpsrv)
+
+
+/* exit our class
+ * rgerhards, 2008-03-10
+ */
+BEGINObjClassExit(tcpsrv, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(tcpsrv)
+ /* release objects we no longer need */
+ objRelease(tcps_sess, DONT_LOAD_LIB);
+ objRelease(conf, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrms, DONT_LOAD_LIB);
+ objRelease(nssel, DONT_LOAD_LIB);
+ objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(net, LM_NET_FILENAME);
+ENDObjClassExit(tcpsrv)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-29
+ */
+BEGINObjClassInit(tcpsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(netstrm, DONT_LOAD_LIB));
+ CHKiRet(objUse(nssel, DONT_LOAD_LIB));
+ CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB));
+ CHKiRet(objUse(conf, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, tcpsrvDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, tcpsrvConstructFinalize);
+ENDObjClassInit(tcpsrv)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* de-init in reverse order! */
+ tcpsrvClassExit();
+ tcps_sessClassExit();
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_LIB_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+
+ /* Initialize all classes that are in our module - this includes ourselfs */
+ CHKiRet(tcps_sessClassInit(pModInfo));
+ CHKiRet(tcpsrvClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+
+/* vim:set ai:
+ */