/* 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 .
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if HAVE_FCNTL_H
#include
#endif
#include "rsyslog.h"
#include "syslogd.h"
#include "cfsysline.h"
#include "module-template.h"
#include "net.h"
#include "srUtils.h"
#include "conf.h"
#include "tcpsrv.h"
#include "obj.h"
#include "errmsg.h"
MODULE_TYPE_LIB
/* defines */
#define TCPSESS_MAX_DEFAULT 200 /* default for nbr of tcp sessions if no number is given */
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(conf)
DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(net)
/* code to free all sockets within a socket table.
* A socket table is a descriptor table where the zero
* element has the count of elements. This is used for
* listening sockets. The socket table itself is also
* freed.
* A POINTER to this structure must be provided, thus
* double indirection!
* rgerhards, 2007-06-28
*/
static void freeAllSockets(int **socks)
{
assert(socks != NULL);
assert(*socks != NULL);
while(**socks) {
dbgprintf("Closing socket %d.\n", (*socks)[**socks]);
close((*socks)[**socks]);
(**socks)--;
}
free(*socks);
*socks = NULL;
}
/* 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
* ", "
* 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(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;
ISOBJ_TYPE_assert(pThis, tcpsrv);
for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i)
if(pThis->pSessions[i] != NULL)
break;
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 iTCPSess;
ISOBJ_TYPE_assert(pThis, tcpsrv);
assert(pThis->pSessions != NULL);
/* close all TCP connections! */
iTCPSess = TCPSessGetNxtSess(pThis, -1);
while(iTCPSess != -1) {
tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
/* now get next... */
iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
}
/* 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 the listen sockets themselfs */
freeAllSockets(&pThis->pSocksLstn);
}
/* Initialize TCP sockets (for listener)
* This function returns either NULL (which means it failed) or
* a pointer to an array of file descriptiors. If the pointer is
* returned, the zeroest element [0] contains the count of valid
* descriptors. The descriptors themself follow in range
* [1] ... [num-descriptors]. It is guaranteed that each of these
* descriptors is valid, at least when this function returns.
* Please note that technically the array may be larger than the number
* of valid pointers stored in it. The memory overhead is minimal, so
* we do not bother to re-allocate an array of the exact size. Logically,
* the array still contains the exactly correct number of descriptors.
*/
static int *create_tcp_socket(tcpsrv_t *pThis)
{
struct addrinfo hints, *res, *r;
int error, maxs, *s, *socks, on = 1;
char *TCPLstnPort;
ISOBJ_TYPE_assert(pThis, tcpsrv);
if(!strcmp(pThis->TCPLstnPort, "0"))
TCPLstnPort = "514";
/* 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 = pThis->TCPLstnPort;
dbgprintf("creating tcp socket on port %s\n", TCPLstnPort);
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(NULL, TCPLstnPort, &hints, &res);
if(error) {
errmsg.LogError(NO_ERRCODE, "%s", gai_strerror(error));
return NULL;
}
/* Count max number of sockets we may open */
for (maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++)
/* EMPTY */;
socks = malloc((maxs+1) * sizeof(int));
if (socks == NULL) {
errmsg.LogError(NO_ERRCODE, "couldn't allocate memory for TCP listen sockets, suspending TCP message reception.");
freeaddrinfo(res);
return NULL;
}
*socks = 0; /* num of sockets counter at start of array */
s = socks + 1;
for (r = res; r != NULL ; r = r->ai_next) {
*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
if (*s < 0) {
if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT))
errmsg.LogError(NO_ERRCODE, "create_tcp_socket(), socket");
/* it is debatable if PF_INET with EAFNOSUPPORT should
* also be ignored...
*/
continue;
}
#ifdef IPV6_V6ONLY
if (r->ai_family == AF_INET6) {
int iOn = 1;
if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&iOn, sizeof (iOn)) < 0) {
errmsg.LogError(NO_ERRCODE, "TCP setsockopt");
close(*s);
*s = -1;
continue;
}
}
#endif
if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof(on)) < 0 ) {
errmsg.LogError(NO_ERRCODE, "TCP setsockopt(REUSEADDR)");
close(*s);
*s = -1;
continue;
}
/* We need to enable BSD compatibility. Otherwise an attacker
* could flood our log files by sending us tons of ICMP errors.
*/
#ifndef OS_BSD
if(net.should_use_so_bsdcompat()) {
if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT,
(char *) &on, sizeof(on)) < 0) {
errmsg.LogError(NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
close(*s);
*s = -1;
continue;
}
}
#endif
if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0)
#ifndef IPV6_V6ONLY
&& (errno != EADDRINUSE)
#endif
) {
errmsg.LogError(NO_ERRCODE, "TCP bind");
close(*s);
*s = -1;
continue;
}
if( listen(*s,pThis->iSessMax / 10 + 5) < 0) {
/* If the listen fails, it most probably fails because we ask
* for a too-large backlog. So in this case we first set back
* to a fixed, reasonable, limit that should work. Only if
* that fails, too, we give up.
*/
errmsg.LogError(NO_ERRCODE, "listen with a backlog of %d failed - retrying with default of 32.",
pThis->iSessMax / 10 + 5);
if(listen(*s, 32) < 0) {
errmsg.LogError(NO_ERRCODE, "TCP listen, suspending tcp inet");
close(*s);
*s = -1;
continue;
}
}
(*socks)++;
s++;
}
if(res != NULL)
freeaddrinfo(res);
if(Debug && *socks != maxs)
dbgprintf("We could initialize %d TCP listen sockets out of %d we received "
"- this may or may not be an error indication.\n", *socks, maxs);
if(*socks == 0) {
errmsg.LogError(NO_ERRCODE, "No TCP listen socket could successfully be initialized, "
"message reception via TCP disabled.\n");
free(socks);
return(NULL);
}
/* 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(NO_ERRCODE, "Could not initialize TCP session table, suspending TCP message reception.");
freeAllSockets(&socks); /* prevent a socket leak */
return(NULL);
}
return(socks);
}
/* 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, int fd)
{
DEFiRet;
tcps_sess_t *pSess;
int newConn;
int iSess = -1;
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
uchar fromHost[NI_MAXHOST];
uchar fromHostFQDN[NI_MAXHOST];
ISOBJ_TYPE_assert(pThis, tcpsrv);
newConn = accept(fd, (struct sockaddr*) &addr, &addrlen);
if (newConn < 0) {
errmsg.LogError(NO_ERRCODE, "tcp accept, ignoring error and connection request");
ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
//was: return -1;
}
/* Add to session list */
iSess = TCPSessTblFindFreeSpot(pThis);
if(iSess == -1) {
errno = 0;
errmsg.LogError(NO_ERRCODE, "too many tcp sessions - dropping incoming request");
close(newConn);
ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
//was: return -1;
} 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 */
if(net.cvthname(&addr, fromHost, fromHostFQDN) != RS_RET_OK) {
/* we seem to have something malicous - at least we
* are now told to discard the connection request.
* Error message has been generated by cvthname.
*/
close (newConn);
ABORT_FINALIZE(RS_RET_ERR); // TODO: better error code
//was: return -1;
}
/* Here we check if a host is permitted to send us
* syslog 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
*/
RUNLOG_VAR("%p", ppSess);
RUNLOG_VAR("%p", pSess);
if(!pThis->pIsPermittedHost((struct sockaddr*) &addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) {
dbgprintf("%s is not an allowed sender\n", (char *) fromHostFQDN);
if(option_DisallowWarning) {
errno = 0;
errmsg.LogError(NO_ERRCODE, "TCP message from disallowed sender %s discarded",
(char*)fromHost);
}
close(newConn);
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, fromHost));
CHKiRet(tcps_sess.SetSock(pSess, newConn));
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;
finalize_it:
if(iRet != RS_RET_OK) {
if(iSess != -1) {
if(pThis->pSessions[iSess] != NULL)
tcps_sess.Destruct(&pThis->pSessions[iSess]);
}
iSess = -1; // TODO: change this to be fully iRet compliant ;)
}
RETiRet;
}
/* This function is called to gather input.
*/
static rsRetVal
Run(tcpsrv_t *pThis)
{
DEFiRet;
int maxfds;
int nfds;
int i;
int iTCPSess;
fd_set readfds;
tcps_sess_t *pNewSess;
ISOBJ_TYPE_assert(pThis, tcpsrv);
/* this is an endless loop - it is terminated when the thread is
* signalled to do so. This, however, is handled by the framework,
* right into the sleep below.
*/
while(1) {
maxfds = 0;
FD_ZERO (&readfds);
/* Add the TCP listen sockets to the list of read descriptors.
*/
if(pThis->pSocksLstn != NULL && *pThis->pSocksLstn) {
for (i = 0; i < *pThis->pSocksLstn; i++) {
/* The if() below is theoretically not needed, but I leave it in
* so that a socket may become unsuable during execution. That
* feature is not yet supported by the current code base.
*/
if (pThis->pSocksLstn[i+1] != -1) {
if(Debug)
net.debugListenInfo(pThis->pSocksLstn[i+1], "TCP");
FD_SET(pThis->pSocksLstn[i+1], &readfds);
if(pThis->pSocksLstn[i+1]>maxfds) maxfds=pThis->pSocksLstn[i+1];
}
}
/* do the sessions */
iTCPSess = TCPSessGetNxtSess(pThis, -1);
while(iTCPSess != -1) {
int fdSess;
fdSess = pThis->pSessions[iTCPSess]->sock; // TODO: NOT CLEAN!, use method
dbgprintf("Adding TCP Session %d\n", fdSess);
FD_SET(fdSess, &readfds);
if (fdSess>maxfds) maxfds=fdSess;
/* now get next... */
iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
}
}
if(Debug) {
// TODO: name in dbgprintf!
dbgprintf("-------- calling select, active file descriptors (max %d): ", maxfds);
for (nfds = 0; nfds <= maxfds; ++nfds)
if ( FD_ISSET(nfds, &readfds) )
dbgprintf("%d ", nfds);
dbgprintf("\n");
}
/* wait for io to become ready */
nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
for (i = 0; i < *pThis->pSocksLstn; i++) {
if (FD_ISSET(pThis->pSocksLstn[i+1], &readfds)) {
dbgprintf("New connect on TCP inetd socket: #%d\n", pThis->pSocksLstn[i+1]);
RUNLOG_VAR("%p", &pNewSess);
SessAccept(pThis, &pNewSess, pThis->pSocksLstn[i+1]);
--nfds; /* indicate we have processed one */
}
}
/* now check the sessions */
iTCPSess = TCPSessGetNxtSess(pThis, -1);
while(nfds && iTCPSess != -1) {
int fdSess;
int state;
fdSess = pThis->pSessions[iTCPSess]->sock; // TODO: not clean, use method
if(FD_ISSET(fdSess, &readfds)) {
char buf[MAXLINE];
dbgprintf("tcp session socket with new data: #%d\n", fdSess);
/* Receive message */
state = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf));
if(state == 0) {
pThis->pOnRegularClose(pThis->pSessions[iTCPSess]);
tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
} else if(state == -1) {
errmsg.LogError(NO_ERRCODE, "TCP session %d will be closed, error ignored\n", fdSess);
pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
} else {
/* valid data received, process it! */
if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, state) != RS_RET_OK) {
/* in this case, something went awfully wrong.
* We are instructed to terminate the session.
*/
errmsg.LogError(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]);
}
}
--nfds; /* indicate we have processed one */
}
iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
}
}
RETiRet;
}
/* Standard-Constructor
*/
BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
pThis->pSocksLstn = NULL;
pThis->iSessMax = 200; /* TODO: useful default ;) */
ENDobjConstruct(tcpsrv)
/* ConstructionFinalizer
*/
static rsRetVal
tcpsrvConstructFinalize(tcpsrv_t __attribute__((unused)) *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcpsrv);
pThis->pSocksLstn = pThis->OpenLstnSocks(pThis);
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);
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, int (*pRcvData)(tcps_sess_t*, char*, size_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, int* (*pCB)(tcpsrv_t*))
{
DEFiRet;
pThis->OpenLstnSocks = pCB;
RETiRet;
}
static rsRetVal
SetUsrP(tcpsrv_t *pThis, void *pUsr)
{
DEFiRet;
pThis->pUsr = pUsr;
RETiRet;
}
/* 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->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(errmsg, CORE_COMPONENT);
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(tcps_sess, DONT_LOAD_LIB));
CHKiRet(objUse(conf, 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:
*/