summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2008-04-28 09:24:05 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2008-04-28 09:24:05 +0200
commit434e404a6c7181a2b7efa9840506e8963665bdd6 (patch)
treee332d2a95705cd4db5e7333a749c218735e2c2e7
parent898d69037de106be8f5b14093daee409f8f7a88f (diff)
parent858f1efd05a2f3c8bc6a0edf8a01f40e27f02407 (diff)
downloadrsyslog-434e404a6c7181a2b7efa9840506e8963665bdd6.tar.gz
rsyslog-434e404a6c7181a2b7efa9840506e8963665bdd6.tar.xz
rsyslog-434e404a6c7181a2b7efa9840506e8963665bdd6.zip
Merge branch 'klogd-bug'
-rw-r--r--ChangeLog2
-rw-r--r--ca.pem14
-rw-r--r--configure.ac77
-rw-r--r--doc/src/queueWorkerLogic.dia (renamed from doc/queueWorkerLogic.dia)bin3334 -> 3334 bytes
-rw-r--r--doc/src/tls.diabin0 -> 4656 bytes
-rw-r--r--plugins/imklog/ksym.c62
-rw-r--r--plugins/imklog/linux.c3
-rw-r--r--plugins/imtcp/imtcp.c12
-rw-r--r--plugins/omgssapi/omgssapi.c23
-rw-r--r--runtime/Makefile.am59
-rw-r--r--runtime/glbl.c27
-rw-r--r--runtime/glbl.h1
-rw-r--r--runtime/net.c1
-rw-r--r--runtime/netstrm.c305
-rw-r--r--runtime/netstrm.h61
-rw-r--r--runtime/netstrms.c200
-rw-r--r--runtime/netstrms.h52
-rw-r--r--runtime/nsd.h72
-rw-r--r--runtime/nsd_gtls.c444
-rw-r--r--runtime/nsd_gtls.h49
-rw-r--r--runtime/nsd_ptcp.c675
-rw-r--r--runtime/nsd_ptcp.h47
-rw-r--r--runtime/nsdsel_gtls.c184
-rw-r--r--runtime/nsdsel_gtls.h45
-rw-r--r--runtime/nsdsel_ptcp.c222
-rw-r--r--runtime/nsdsel_ptcp.h47
-rw-r--r--runtime/nssel.c229
-rw-r--r--runtime/nssel.h55
-rw-r--r--runtime/obj-types.h15
-rw-r--r--runtime/obj.c2
-rw-r--r--runtime/rsyslog.h24
-rw-r--r--tcpclt.c1
-rw-r--r--tcpclt.h1
-rw-r--r--tcps_sess.c45
-rw-r--r--tcps_sess.h8
-rw-r--r--tcpsrv.c410
-rw-r--r--tcpsrv.h20
-rw-r--r--tools/Makefile.am42
-rw-r--r--tools/omfwd.c586
-rw-r--r--tools/syslogd.c11
40 files changed, 3409 insertions, 724 deletions
diff --git a/ChangeLog b/ChangeLog
index 52c637c7..5bcc3d50 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,8 @@
---------------------------------------------------------------------------
Version 3.19.0 (rgerhards), 2008-04-??
- begins new devel branch version
+- partly rewritten and improved omfwd among others, now loads TCP
+ code only if this is actually necessary
- implemented im3195, the RFC3195 input as a plugin
- split of a "runtime library" for rsyslog - this is not yet a clean
model, because some modularization is still outstanding. In theory,
diff --git a/ca.pem b/ca.pem
new file mode 100644
index 00000000..747250ca
--- /dev/null
+++ b/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaegAwIBAgIBADALBgkqhkiG9w0BAQUwRTELMAkGA1UEBhMCR1IxDDAK
+BgNVBAoTA0ZTRjEPMA0GA1UECxMGR05VVExTMRcwFQYDVQQDEw5HTlVUTFMgVEVT
+VCBDQTAeFw0wNDA2MjgyMjQ0MDBaFw0wNzAzMjQyMjQ0MDBaMEUxCzAJBgNVBAYT
+AkdSMQwwCgYDVQQKEwNGU0YxDzANBgNVBAsTBkdOVVRMUzEXMBUGA1UEAxMOR05V
+VExTIFRFU1QgQ0EwgZwwCwYJKoZIhvcNAQEBA4GMADCBiAKBgMK0cY9Tap5F7DXX
+tu7HpHlZtu+zqfofyLJSIBpUdbiwFIGzB486stbog0mpiy32mGIG5hNlpcRMJMVm
+MmZ1RueqQWR+vdDroBoV199zAZQVww1NmmHi/Wtxa6x9SsXdya+SnoC8KI/V3EKx
+gYG0hYAuYWNA9JnntTU0xCwWOBaPAgMBAAGjQzBBMA8GA1UdEwEB/wQFMAMBAf8w
+DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUeesbb6Nm5nnh0eK129onBkUpCkgw
+CwYJKoZIhvcNAQEFA4GBAGCCzUSCVZOXffm/KFxbyT2Lrltyzqlr0Oknp55eNAIk
+fy+m/viSOmoTCaK9Gmtk3eMAxIeZ8U7TDKrbrxx/NSsggbypqa3EMMwr2JH9kzAZ
+eluQ0vEVqfvRq5jzjuORYYhl7VgqpU0/ctvI3b+9tCZAOCcUX0HPvNweAzYjnkDi
+-----END CERTIFICATE-----
diff --git a/configure.ac b/configure.ac
index ddb91b11..d436cec8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -433,42 +433,6 @@ AC_SUBST(libdbi_cflags)
AC_SUBST(libdbi_libs)
-# openssl support
-AC_ARG_ENABLE(openssl,
- [AS_HELP_STRING([--enable-openssl],[Enable openssl support @<:@default=yes@:>@])],
- [case "${enableval}" in
- yes) enable_openssl="yes" ;;
- no) enable_openssl="no" ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-openssl) ;;
- esac],
- [enable_openssl=no]
-)
-if test "x$enable_openssl" = "xyes"; then
- AC_CHECK_HEADERS(
- [openssl/ssl.h],,
- [AC_MSG_FAILURE([openssl is missing])]
- )
- AC_CHECK_LIB(
- [crypto],
- [CRYPTO_new_ex_data],
- [openssl_cflags=""
- openssl_libs="-lcrypto"
- ],
- [AC_MSG_FAILURE([library 'crypto' is missing (needed for openssl)])]
- )
- AC_CHECK_LIB(
- [ssl],
- [SSL_library_init],
- [ openssl_libs+="-lssl"
- ],
- [AC_MSG_FAILURE([library 'ssl' is missing (needed for openssl)])]
- )
-fi
-AM_CONDITIONAL(ENABLE_OPENSSL, test x$enable_openssl = xyes)
-AC_SUBST(openssl_cflags)
-AC_SUBST(openssl_libs)
-
-
# SNMP support
AC_ARG_ENABLE(snmp,
[AS_HELP_STRING([--enable-snmp],[Enable SNMP support @<:@default=no@:>@])],
@@ -498,6 +462,44 @@ AC_SUBST(snmp_cflags)
AC_SUBST(snmp_libs)
+# GNUtls support
+AC_ARG_ENABLE(gnutls,
+ [AS_HELP_STRING([--enable-gnutls],[Enable GNU TLS support @<:@default=yes@:>@])],
+ [case "${enableval}" in
+ yes) enable_gnutls="yes" ;;
+ no) enable_gnutls="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-gnutls) ;;
+ esac],
+ [enable_gnutls=no]
+)
+if test "x$enable_gnutls" = "xyes"; then
+ AC_CHECK_HEADERS(
+ [gnutls/gnutls.h],,
+ [AC_MSG_FAILURE([GNUTls is missing])]
+ )
+ AC_CHECK_PROG(
+ [HAVE_GNUTLS_CONFIG],
+ [libgnutls-config],
+ [yes],,,
+ )
+ if test "x${HAVE_GNUTLS_CONFIG}" != "xyes"; then
+ AC_MSG_FAILURE([libgnutls-config not found in PATH])
+ fi
+ AC_CHECK_LIB(
+ [gnutls],
+ [gnutls_check_version],
+ [gnutls_cflags=`libgnutls-config --cflags`
+ gnutls_libs=`libgnutls-config --libs`
+ ],
+ [AC_MSG_FAILURE([GNU TLS library is missing])],
+ [`libgnutls-config --libs`]
+ )
+fi
+AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes)
+AC_SUBST(gnutls_cflags)
+AC_SUBST(gnutls_libs)
+
+
# support for building the rsyslogd runtime
AC_ARG_ENABLE(rsyslogrt,
[AS_HELP_STRING([--enable-rsyslogrt],[Build rsyslogrt @<:@default=yes@:>@])],
@@ -561,7 +563,6 @@ AC_SUBST(RELP_CFLAGS)
AC_SUBST(RELP_LIBS)
# RFC 3195 support
-# WARNING: THIS IS NOT REALLY PRESENT YET - needs to be build manually!
AC_ARG_ENABLE(rfc3195,
[AS_HELP_STRING([--enable-rfc3195],[Enable RFC3195 support @<:@default=no@:>@])],
[case "${enableval}" in
@@ -660,10 +661,10 @@ echo "file input module enabled: $enable_imfile"
echo "input template module will be compiled: $enable_imtemplate"
echo "Large file support enabled: $enable_largefile"
echo "Networking support enabled: $enable_inet"
+echo "GnuTLS network stream driver enabled: $enable_gnutls"
echo "Enable GSSAPI Kerberos 5 support: $want_gssapi_krb5"
echo "Debug mode enabled: $enable_debug"
echo "Runtime Instrumentation enabled: $enable_rtinst"
-echo "openssl enabled: $enable_openssl"
echo "valgrind support settings enabled: $enable_valgrind"
echo "rsyslog runtime will be built: $enable_rsyslogrt"
echo "rsyslogd will be built: $enable_rsyslogd"
diff --git a/doc/queueWorkerLogic.dia b/doc/src/queueWorkerLogic.dia
index 068ea50c..068ea50c 100644
--- a/doc/queueWorkerLogic.dia
+++ b/doc/src/queueWorkerLogic.dia
Binary files differ
diff --git a/doc/src/tls.dia b/doc/src/tls.dia
new file mode 100644
index 00000000..77e5d185
--- /dev/null
+++ b/doc/src/tls.dia
Binary files differ
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
index 1c2af124..34ce909e 100644
--- a/plugins/imklog/ksym.c
+++ b/plugins/imklog/ksym.c
@@ -138,7 +138,7 @@ static char *system_maps[] =
/* Function prototypes. */
-static char * FindSymbolFile(void);
+static char *FindSymbolFile(void);
static int AddSymbol(unsigned long, char*);
static void FreeSymbols(void);
static int CheckVersion(char *);
@@ -173,29 +173,28 @@ extern int InitKsyms(char *mapfile)
auto FILE *sym_file;
+ BEGINfunc
/* Check and make sure that we are starting with a clean slate. */
if ( num_syms > 0 )
FreeSymbols();
- /*
- * Search for and open the file containing the kernel symbols.
- */
- if ( mapfile != (char *) 0 ) {
- if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
+ /* Search for and open the file containing the kernel symbols. */
+ if ( mapfile != NULL ) {
+ if ( (sym_file = fopen(mapfile, "r")) == NULL )
{
imklogLogIntMsg(LOG_WARNING, "Cannot open map file: %s.", mapfile);
return(0);
}
} else {
- if ( (mapfile = FindSymbolFile()) == (char *) 0 ) {
+ if ( (mapfile = FindSymbolFile()) == NULL ) {
imklogLogIntMsg(LOG_WARNING, "Cannot find map file.");
dbgprintf("Cannot find map file.\n");
return(0);
}
- if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 ) {
+ if ( (sym_file = fopen(mapfile, "r")) == NULL ) {
imklogLogIntMsg(LOG_WARNING, "Cannot open map file.");
dbgprintf("Cannot open map file.\n");
return(0);
@@ -248,6 +247,7 @@ extern int InitKsyms(char *mapfile)
}
fclose(sym_file);
+ ENDfunc
return(1);
}
@@ -292,44 +292,42 @@ extern void DeinitKsyms(void)
**************************************************************************/
static char *FindSymbolFile(void)
{
- auto char *file = (char *) 0,
+ auto char *file = NULL,
**mf = system_maps;
-
auto struct utsname utsname;
static char mysymfile[100];
+ auto FILE *sym_file = NULL;
+ BEGINfunc
- auto FILE *sym_file = (FILE *) 0;
-
- if ( uname(&utsname) < 0 ) {
+ if(uname(&utsname) < 0) {
imklogLogIntMsg(LOG_ERR, "Cannot get kernel version information.");
return(0);
}
dbgprintf("Searching for symbol map.\n");
- for(mf = system_maps; *mf != (char *) 0 && file == (char *) 0; ++mf) {
-
+ for(mf = system_maps; *mf != NULL && file == NULL; ++mf) {
snprintf(mysymfile, sizeof(mysymfile), "%s-%s", *mf, utsname.release);
dbgprintf("Trying %s.\n", mysymfile);
- if ( (sym_file = fopen(mysymfile, "r")) != (FILE *) 0 ) {
- if (CheckMapVersion(mysymfile) == 1)
+ if((sym_file = fopen(mysymfile, "r")) != NULL) {
+ if(CheckMapVersion(mysymfile) == 1)
file = mysymfile;
fclose(sym_file);
}
- if (sym_file == (FILE *) 0 || file == (char *) 0) {
+ if(sym_file == NULL || file == NULL) {
sprintf (mysymfile, "%s", *mf);
dbgprintf("Trying %s.\n", mysymfile);
- if ( (sym_file = fopen(mysymfile, "r")) != (FILE *) 0 ) {
+ if((sym_file = fopen(mysymfile, "r")) != NULL ) {
if (CheckMapVersion(mysymfile) == 1)
file = mysymfile;
fclose(sym_file);
}
}
-
}
/* At this stage of the game we are at the end of the symbol tables. */
dbgprintf("End of search list encountered.\n");
+ ENDfunc
return(file);
}
@@ -464,7 +462,7 @@ static int CheckMapVersion(char *fname)
auto char type,
sym[512];
- if ( (sym_file = fopen(fname, "r")) != (FILE *) 0 ) {
+ if ( (sym_file = fopen(fname, "r")) != NULL ) {
/*
* At this point a map file was successfully opened. We
* now need to search this file and look for version
@@ -527,7 +525,7 @@ static int AddSymbol(unsigned long address, char *symbol)
/* Then the space for the symbol. */
sym_array[num_syms].name = (char *) malloc(strlen(symbol)*sizeof(char) + 1);
- if ( sym_array[num_syms].name == (char *) 0 )
+ if ( sym_array[num_syms].name == NULL )
return(0);
sym_array[num_syms].value = address;
@@ -566,13 +564,13 @@ char * LookupSymbol(unsigned long value, struct symbol *sym)
struct symbol ksym, msym;
if (!sym_array)
- return((char *) 0);
+ return(NULL);
last = sym_array[0].name;
ksym.offset = 0;
ksym.size = 0;
if ( value < sym_array[0].value )
- return((char *) 0);
+ return(NULL);
for(lp = 0; lp <= num_syms; ++lp) {
if ( sym_array[lp].value > value ) {
@@ -587,7 +585,7 @@ char * LookupSymbol(unsigned long value, struct symbol *sym)
name = LookupModuleSymbol(value, &msym);
if ( ksym.offset == 0 && msym.offset == 0 ) {
- return((char *) 0);
+ return(NULL);
}
if ( ksym.offset == 0 || msym.offset < 0 ||
@@ -602,7 +600,7 @@ char * LookupSymbol(unsigned long value, struct symbol *sym)
}
- return((char *) 0);
+ return(NULL);
}
/**************************************************************************
@@ -683,7 +681,7 @@ extern char *ExpandKadds(char *line, char *el)
* open for patches.
*/
if ( i_am_paranoid &&
- (strstr(line, "Oops:") != (char *) 0) && !InitMsyms() )
+ (strstr(line, "Oops:") != NULL) && !InitMsyms() )
imklogLogIntMsg(LOG_WARNING, "Cannot load kernel module symbols.\n");
@@ -692,7 +690,7 @@ extern char *ExpandKadds(char *line, char *el)
* messages in this line.
*/
if ( (num_syms == 0) ||
- (kp = strstr(line, "[<")) == (char *) 0 ) {
+ (kp = strstr(line, "[<")) == NULL ) {
#ifdef __sparc__
if (num_syms) {
/* On SPARC, register dumps do not have the [< >] characters in it.
@@ -780,7 +778,7 @@ extern char *ExpandKadds(char *line, char *el)
*elp++ = *sl++;
/* Now poised at a kernel delimiter. */
- if ( (kp = strstr(sl, ">]")) == (char *) 0 ) {
+ if ( (kp = strstr(sl, ">]")) == NULL ) {
strcpy(el, sl);
return(el);
}
@@ -788,7 +786,7 @@ extern char *ExpandKadds(char *line, char *el)
strncpy(num,sl+1,kp-sl-1);
num[kp-sl-1] = '\0';
value = strtoul(num, (char **) 0, 16);
- if ( (symbol = LookupSymbol(value, &sym)) == (char *) 0 )
+ if ( (symbol = LookupSymbol(value, &sym)) == NULL )
symbol = sl;
strcat(elp, symbol);
@@ -805,10 +803,10 @@ extern char *ExpandKadds(char *line, char *el)
strncat(elp, kp, value);
elp += value;
sl = kp + value;
- if ( (kp = strstr(sl, "[<")) == (char *) 0 )
+ if ( (kp = strstr(sl, "[<")) == NULL )
strcat(elp, sl);
}
- while ( kp != (char *) 0);
+ while ( kp != NULL);
dbgprintf("Expanded line: %s\n", el);
return(el);
diff --git a/plugins/imklog/linux.c b/plugins/imklog/linux.c
index 32cf70c4..8e4d3388 100644
--- a/plugins/imklog/linux.c
+++ b/plugins/imklog/linux.c
@@ -499,7 +499,8 @@ rsRetVal klogWillRun(void)
symbol_lookup = (InitKsyms(symfile) == 1);
symbol_lookup |= InitMsyms();
if (symbol_lookup == 0) {
- imklogLogIntMsg(LOG_WARNING, "cannot find any symbols, turning off symbol lookups\n");
+ dbgprintf("cannot find any symbols, turning off symbol lookups\n");
+ imklogLogIntMsg(LOG_WARNING, "cannot find any symbols, turning off symbol lookups");
}
}
}
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index b7f8f0b5..1e599d14 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -43,6 +43,7 @@
#include "cfsysline.h"
#include "module-template.h"
#include "net.h"
+#include "netstrm.h"
#include "tcpsrv.h"
MODULE_TYPE_INPUT
@@ -52,6 +53,7 @@ DEF_IMOD_STATIC_DATA
DEFobjCurrIf(tcpsrv)
DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(net)
+DEFobjCurrIf(netstrm)
/* Module static data */
static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
@@ -70,7 +72,7 @@ isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((u
}
-static int*
+static rsRetVal
doOpenLstnSocks(tcpsrv_t *pSrv)
{
ISOBJ_TYPE_assert(pSrv, tcpsrv);
@@ -81,10 +83,12 @@ doOpenLstnSocks(tcpsrv_t *pSrv)
static int
doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf)
{
- int state;
+ ssize_t state;
assert(pSess != NULL);
- state = recv(pSess->sock, buf, lenBuf, 0);
+ state = lenBuf;
+ if(netstrm.Rcv(pSess->pStrm, (uchar*) buf, &state) != RS_RET_OK)
+ state = -1; // TODO: move this function to an iRet interface! 2008-04-23
return state;
}
@@ -172,6 +176,7 @@ CODESTARTmodExit
/* release objects we used */
objRelease(net, LM_NET_FILENAME);
+ objRelease(netstrm, LM_NETSTRM_FILENAME);
objRelease(tcps_sess, LM_TCPSRV_FILENAME);
objRelease(tcpsrv, LM_TCPSRV_FILENAME);
ENDmodExit
@@ -199,6 +204,7 @@ CODEmodInit_QueryRegCFSLineHdlr
pOurTcpsrv = NULL;
/* request objects we use */
CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index 3f6600ca..6d419de0 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -58,17 +58,6 @@
MODULE_TYPE_OUTPUT
-#define INET_SUSPEND_TIME 60
-/* equal to 1 minute - TODO: see if we can get rid of this now that we have
- * the retry intervals in the engine -- rgerhards, 2008-03-12
- */
-
-#define INET_RETRY_MAX 30 /* maximum of retries for gethostbyname() */
- /* was 10, changed to 30 because we reduced INET_SUSPEND_TIME by one third. So
- * this "fixes" some of implications of it (see comment on INET_SUSPEND_TIME).
- * rgerhards, 2005-07-26
- * TODO: this needs to be reviewed in spite of the new engine, too -- rgerhards, 2008-03-12
- */
/* internal structures
*/
@@ -86,11 +75,9 @@ typedef struct _instanceData {
eDestFORW_SUSP,
eDestFORW_UNKN
} eDestState;
- int iRtryCnt;
struct addrinfo *f_addr;
int compressionLevel; /* 0 - no compression, else level for zlib */
char *port;
- time_t ttSuspend; /* time selector was suspended */
tcpclt_t *pTCPClt; /* our tcpclt object */
gss_ctx_id_t gss_context;
OM_uint32 gss_flags;
@@ -174,8 +161,6 @@ CODESTARTdbgPrintInstInfo
ENDdbgPrintInstInfo
-/* CODE FOR SENDING TCP MESSAGES */
-
/* This function is called immediately before a send retry is attempted.
* It shall clean up whatever makes sense.
* rgerhards, 2007-12-28
@@ -207,9 +192,7 @@ static rsRetVal TCPSendGSSInit(void *pvData)
base = (gss_base_service_name == NULL) ? "host" : gss_base_service_name;
out_tok.length = strlen(pData->f_hname) + strlen(base) + 2;
- if ((out_tok.value = malloc(out_tok.length)) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKmalloc(out_tok.value = malloc(out_tok.length));
strcpy(out_tok.value, base);
strcat(out_tok.value, "@");
strcat(out_tok.value, pData->f_hname);
@@ -371,7 +354,6 @@ static rsRetVal doTryResume(instanceData *pData)
getFwdSyslogPt(pData), &hints, &res)) == 0) {
dbgprintf("%s found, resuming.\n", pData->f_hname);
pData->f_addr = res;
- pData->iRtryCnt = 0;
pData->eDestState = eDestFORW;
} else {
iRet = RS_RET_SUSPENDED;
@@ -410,7 +392,6 @@ CODESTARTdoAction
case eDestFORW:
dbgprintf(" %s:%s/%s\n", pData->f_hname, getFwdSyslogPt(pData), "tcp-gssapi");
- pData->ttSuspend = time(NULL);
psz = (char*) ppString[0];
l = strlen((char*) psz);
if (l > MAXLINE)
@@ -613,8 +594,6 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
hints.ai_socktype = SOCK_STREAM;
if( (error = getaddrinfo(pData->f_hname, getFwdSyslogPt(pData), &hints, &res)) != 0) {
pData->eDestState = eDestFORW_UNKN;
- pData->iRtryCnt = INET_RETRY_MAX;
- pData->ttSuspend = time(NULL);
} else {
pData->eDestState = eDestFORW;
pData->f_addr = res;
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index 73418fdf..dcb475d8 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -11,6 +11,7 @@ librsyslog_la_SOURCES = \
syslogd-types.h \
module-template.h \
obj-types.h \
+ nsd.h \
glbl.h \
glbl.c \
msg.c \
@@ -75,18 +76,70 @@ if ENABLE_REGEXP
pkglib_LTLIBRARIES += lmregexp.la
lmregexp_la_SOURCES = regexp.c regexp.h
lmregexp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
-lmregexp_la_LDFLAGS = -module -avoid-version $(rsrt_libs)
+lmregexp_la_LDFLAGS = -module -avoid-version
lmregexp_la_LIBADD =
endif
if ENABLE_INET
-pkglib_LTLIBRARIES += lmnet.la
+pkglib_LTLIBRARIES += lmnet.la lmnetstrms.la lmnetstrm.la lmnssel.la
#
# network support
#
lmnet_la_SOURCES = net.c net.h
lmnet_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
-lmnet_la_LDFLAGS = -module -avoid-version $(rsrt_libs)
+lmnet_la_LDFLAGS = -module -avoid-version
lmnet_la_LIBADD =
+# network stream master class and stream factory
+lmnetstrms_la_SOURCES = netstrms.c netstrms.h
+lmnetstrms_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnetstrms_la_LDFLAGS = -module -avoid-version
+lmnetstrms_la_LIBADD =
+
+# individual network streams
+lmnetstrm_la_SOURCES = netstrm.c netstrm.h
+lmnetstrm_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnetstrm_la_LDFLAGS = -module -avoid-version
+lmnetstrm_la_LIBADD =
+
+# network stream select support (a helper class)
+lmnssel_la_SOURCES = nssel.c nssel.h
+lmnssel_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnssel_la_LDFLAGS = -module -avoid-version
+lmnssel_la_LIBADD =
+
+# netstream drivers
+
+# plain tcp driver - main driver
+pkglib_LTLIBRARIES += lmnsd_ptcp.la
+lmnsd_ptcp_la_SOURCES = nsd_ptcp.c nsd_ptcp.h
+lmnsd_ptcp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnsd_ptcp_la_LDFLAGS = -module -avoid-version
+lmnsd_ptcp_la_LIBADD =
+
+# select interface for ptcp driver
+pkglib_LTLIBRARIES += lmnsdsel_ptcp.la
+lmnsdsel_ptcp_la_SOURCES = nsdsel_ptcp.c nsdsel_ptcp.h
+lmnsdsel_ptcp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnsdsel_ptcp_la_LDFLAGS = -module -avoid-version
+lmnsdsel_ptcp_la_LIBADD =
endif # if ENABLE_INET
+
+#
+# GnuTLS netstream driver
+#
+if ENABLE_GNUTLS
+pkglib_LTLIBRARIES += lmnsd_gtls.la
+lmnsd_gtls_la_SOURCES = nsd_gtls.c nsd_gtls.h
+lmnsd_gtls_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) $(gnutls_cflags)
+lmnsd_gtls_la_LDFLAGS = -module -avoid-version
+lmnsd_gtls_la_LIBADD = $(gnutls_libs)
+#
+# select interface for gtls driver
+pkglib_LTLIBRARIES += lmnsdsel_gtls.la
+lmnsdsel_gtls_la_SOURCES = nsdsel_gtls.c nsdsel_gtls.h
+lmnsdsel_gtls_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnsdsel_gtls_la_LDFLAGS = -module -avoid-version
+lmnsdsel_gtls_la_LIBADD =
+endif
+
diff --git a/runtime/glbl.c b/runtime/glbl.c
index 047fd611..787b6ab7 100644
--- a/runtime/glbl.c
+++ b/runtime/glbl.c
@@ -38,6 +38,13 @@
#include "cfsysline.h"
#include "glbl.h"
+/* some defaults */
+#ifndef DFLT_NETSTRM_DRVR
+// TESTING ONLY# define DFLT_NETSTRM_DRVR ((uchar*)"lmnsd_ptcp")
+#warning "define must be restored for non-testing!"
+# define DFLT_NETSTRM_DRVR ((uchar*)"lmnsd_gtls")
+#endif
+
/* static data */
DEFobjStaticHelpers
@@ -54,6 +61,7 @@ static uchar *LocalHostName = NULL;/* our hostname - read-only after startup */
static uchar *LocalDomain; /* our local domain name - read-only after startup */
static char **StripDomains = NULL;/* these domains may be stripped before writing logs - r/o after s.u., never touched by init */
static char **LocalHosts = NULL;/* these hosts are logged with their hostname - read-only after startup, never touched by init */
+static uchar *pszDfltNetstrmDrvr = NULL; /* module name of default netstream driver */
/* define a macro for the simple properties' set and get functions
@@ -84,6 +92,7 @@ SIMP_PROP(StripDomains, StripDomains, char**)
SIMP_PROP(LocalHosts, LocalHosts, char**)
SIMP_PROP_SET(LocalHostName, LocalHostName, uchar*)
+SIMP_PROP_SET(DfltNetstrmDrvr, pszDfltNetstrmDrvr, uchar*) // TODO: use custom function which frees existing value
#undef SIMP_PROP
#undef SIMP_PROP_SET
@@ -99,8 +108,7 @@ GetLocalHostName(void)
}
-/* return the current working directory.
- */
+/* return the current working directory */
static uchar*
GetWorkDir(void)
{
@@ -108,6 +116,14 @@ GetWorkDir(void)
}
+/* return the current default netstream driver */
+static uchar*
+GetDfltNetstrmDrvr(void)
+{
+ return(pszDfltNetstrmDrvr == NULL ? DFLT_NETSTRM_DRVR : pszWorkDir);
+}
+
+
/* queryInterface function
* rgerhards, 2008-02-21
*/
@@ -134,6 +150,7 @@ CODESTARTobjQueryInterface(glbl)
SIMP_PROP(LocalDomain)
SIMP_PROP(StripDomains)
SIMP_PROP(LocalHosts)
+ SIMP_PROP(DfltNetstrmDrvr)
#undef SIMP_PROP
finalize_it:
ENDobjQueryInterface(glbl)
@@ -144,6 +161,10 @@ ENDobjQueryInterface(glbl)
*/
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
+ if(pszDfltNetstrmDrvr != NULL) {
+ free(pszDfltNetstrmDrvr);
+ pszDfltNetstrmDrvr = NULL;
+ }
if(pszWorkDir != NULL) {
free(pszWorkDir);
pszWorkDir = NULL;
@@ -172,6 +193,8 @@ ENDObjClassInit(glbl)
* rgerhards, 2008-04-17
*/
BEGINObjClassExit(glbl, OBJ_IS_CORE_MODULE) /* class, version */
+ if(pszDfltNetstrmDrvr != NULL)
+ free(pszDfltNetstrmDrvr);
if(pszWorkDir != NULL)
free(pszWorkDir);
if(LocalHostName != NULL)
diff --git a/runtime/glbl.h b/runtime/glbl.h
index d61f9b41..b6864f3d 100644
--- a/runtime/glbl.h
+++ b/runtime/glbl.h
@@ -48,6 +48,7 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
SIMP_PROP(LocalDomain, uchar*)
SIMP_PROP(StripDomains, char**)
SIMP_PROP(LocalHosts, char**)
+ SIMP_PROP(DfltNetstrmDrvr, uchar*)
#undef SIMP_PROP
ENDinterface(glbl)
#define glblCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
diff --git a/runtime/net.c b/runtime/net.c
index 0c02eed4..1d085290 100644
--- a/runtime/net.c
+++ b/runtime/net.c
@@ -666,7 +666,6 @@ gethname(struct sockaddr_storage *f, uchar *pszHostFQDN)
if (error == 0) {
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_flags = AI_NUMERICHOST;
- hints.ai_socktype = SOCK_DGRAM;
/* we now do a lookup once again. This one should fail,
* because we should not have obtained a non-numeric address. If
diff --git a/runtime/netstrm.c b/runtime/netstrm.c
new file mode 100644
index 00000000..be754aae
--- /dev/null
+++ b/runtime/netstrm.c
@@ -0,0 +1,305 @@
+/* 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, 2008 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 "module-template.h"
+#include "obj.h"
+#include "errmsg.h"
+//#include "nsd.h"
+#include "netstrms.h"
+#include "netstrm.h"
+
+MODULE_TYPE_LIB
+
+/* 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(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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+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, LM_NETSTRMS_FILENAME);
+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));
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+
+ /* set our own handlers */
+ENDObjClassInit(netstrm)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ netstrmClassExit();
+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(netstrmClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/netstrm.h b/runtime/netstrm.h
new file mode 100644
index 00000000..160bbb0b
--- /dev/null
+++ b/runtime/netstrm.h
@@ -0,0 +1,61 @@
+/* Definitions for the stream-based netstrmworking class.
+ *
+ * Copyright 2007, 2008 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.
+ */
+
+#ifndef INCLUDED_NETSTRM_H
+#define INCLUDED_NETSTRM_H
+
+#include "nsd.h" /* we need our driver interface to be defined */
+
+/* the netstrm object */
+struct netstrm_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ nsd_t *pDrvrData; /**< the driver's data elements (at most other places, this is called pNsd) */
+ nsd_if_t Drvr; /**< our stream driver */
+ netstrms_t *pNS; /**< pointer to our netstream subsystem object */
+};
+
+
+/* interface */
+BEGINinterface(netstrm) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(netstrm_t **ppThis);
+ rsRetVal (*ConstructFinalize)(netstrm_t *pThis);
+ rsRetVal (*Destruct)(netstrm_t **ppThis);
+ rsRetVal (*AbortDestruct)(netstrm_t **ppThis);
+ rsRetVal (*LstnInit)(netstrms_t *pNS, void *pUsr, rsRetVal(*)(void*,netstrm_t*),
+ uchar *pLstnPort, uchar *pLstnIP, int iSessMax);
+ rsRetVal (*AcceptConnReq)(netstrm_t *pThis, netstrm_t **ppNew);
+ rsRetVal (*Rcv)(netstrm_t *pThis, uchar *pRcvBuf, ssize_t *pLenBuf);
+ rsRetVal (*Send)(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf);
+ rsRetVal (*Connect)(netstrm_t *pThis, int family, unsigned char *port, unsigned char *host);
+ rsRetVal (*GetRemoteHName)(netstrm_t *pThis, uchar **pszName);
+ rsRetVal (*GetRemoteIP)(netstrm_t *pThis, uchar **pszIP);
+ENDinterface(netstrm)
+#define netstrmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+/* prototypes */
+PROTOTYPEObj(netstrm);
+
+/* the name of our library binary */
+#define LM_NETSTRM_FILENAME "lmnetstrm"
+
+#endif /* #ifndef INCLUDED_NETSTRM_H */
diff --git a/runtime/netstrms.c b/runtime/netstrms.c
new file mode 100644
index 00000000..661234e4
--- /dev/null
+++ b/runtime/netstrms.c
@@ -0,0 +1,200 @@
+/* netstrms.c
+ *
+ * Work on this module begung 2008-04-23 by Rainer Gerhards.
+ *
+ * Copyright 2008 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 <errno.h>
+
+#include "rsyslog.h"
+#include "module-template.h"
+#include "obj.h"
+//#include "errmsg.h"
+//#include "net.h"
+#include "nsd.h"
+#include "netstrm.h"
+#include "netstrms.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+//DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(netstrm)
+//DEFobjCurrIf(net)
+
+
+/* load our low-level driver. This must be done before any
+ * driver-specific functions (allmost all...) can be carried
+ * out. Note that the driver's .ifIsLoaded is correctly
+ * initialized by calloc() and we depend on that.
+ * rgerhards, 2008-04-18
+ */
+static rsRetVal
+loadDrvr(netstrms_t *pThis)
+{
+ uchar *pDrvrName;
+ DEFiRet;
+
+ pDrvrName = pThis->pDrvrName;
+ if(pDrvrName == NULL) /* if no drvr name is set, use system default */
+ pDrvrName = glbl.GetDfltNetstrmDrvr();
+
+ pThis->Drvr.ifVersion = nsdCURR_IF_VERSION;
+ /* The pDrvrName+2 below is a hack to obtain the object name. It
+ * safes us to have yet another variable with the name without "lm" in
+ * front of it. If we change the module load interface, we may re-think
+ * about this hack, but for the time being it is efficient and clean
+ * enough. -- rgerhards, 2008-04-18
+ */
+ CHKiRet(obj.UseObj(__FILE__, pDrvrName+2, pDrvrName, (void*) &pThis->Drvr));
+finalize_it:
+ RETiRet;
+}
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(netstrms) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(netstrms)
+
+
+/* destructor for the netstrms object */
+BEGINobjDestruct(netstrms) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(netstrms)
+ if(pThis->pDrvrName != NULL)
+ free(pThis->pDrvrName);
+ENDobjDestruct(netstrms)
+
+
+/* ConstructionFinalizer */
+static rsRetVal
+netstrmsConstructFinalize(netstrms_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, netstrms);
+ CHKiRet(loadDrvr(pThis));
+finalize_it:
+ RETiRet;
+}
+
+
+/* create an instance of a netstrm object. It is initialized with default
+ * values. The current driver is used. The caller may set netstrm properties
+ * and must call ConstructFinalize().
+ */
+static rsRetVal
+CreateStrm(netstrms_t *pThis, netstrm_t **ppStrm)
+{
+ netstrm_t *pStrm = NULL;
+ DEFiRet;
+
+ CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
+ CHKiRet(netstrm.Construct(&pStrm));
+ /* we copy over our driver structure. We could provide a pointer to
+ * ourselves, but that costs some performance on each driver invocation.
+ * As we already have hefty indirection (and thus performance toll), I
+ * prefer to copy over the function pointers here. -- rgerhards, 2008-04-23
+ */
+ memcpy(&pStrm->Drvr, &pThis->Drvr, sizeof(pThis->Drvr));
+ pStrm->pNS = pThis;
+
+ *ppStrm = pStrm;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pStrm != NULL)
+ netstrm.Destruct(&pStrm);
+ }
+ RETiRet;
+}
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(netstrms)
+CODESTARTobjQueryInterface(netstrms)
+ if(pIf->ifVersion != netstrmsCURR_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 = netstrmsConstruct;
+ pIf->ConstructFinalize = netstrmsConstructFinalize;
+ pIf->Destruct = netstrmsDestruct;
+ pIf->CreateStrm = CreateStrm;
+finalize_it:
+ENDobjQueryInterface(netstrms)
+
+
+/* exit our class */
+BEGINObjClassExit(netstrms, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(netstrms)
+ /* release objects we no longer need */
+ //objRelease(net, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(netstrm, LM_NETSTRM_FILENAME);
+ENDObjClassExit(netstrms)
+
+
+/* Initialize the netstrms class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINAbstractObjClassInit(netstrms, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ //CHKiRet(objUse(net, CORE_COMPONENT));
+
+ /* set our own handlers */
+ENDObjClassInit(netstrms)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ netstrmsClassExit();
+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(netstrmsClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/netstrms.h b/runtime/netstrms.h
new file mode 100644
index 00000000..1e920304
--- /dev/null
+++ b/runtime/netstrms.h
@@ -0,0 +1,52 @@
+/* Definitions for the stream-based netstrmsworking class.
+ *
+ * Copyright 2007, 2008 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.
+ */
+
+#ifndef INCLUDED_NETSTRMS_H
+#define INCLUDED_NETSTRMS_H
+
+#include "nsd.h" /* we need our driver interface to be defined */
+
+/* the netstrms object */
+struct netstrms_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ uchar *pDrvrName; /**< nsd driver name to use, or NULL if system default */
+ nsd_if_t Drvr; /**< our stream driver */
+};
+
+
+/* interface */
+BEGINinterface(netstrms) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(netstrms_t **ppThis);
+ rsRetVal (*ConstructFinalize)(netstrms_t *pThis);
+ rsRetVal (*Destruct)(netstrms_t **ppThis);
+ rsRetVal (*CreateStrm)(netstrms_t *pThis, netstrm_t **ppStrm);
+ENDinterface(netstrms)
+#define netstrmsCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+/* prototypes */
+PROTOTYPEObj(netstrms);
+
+/* the name of our library binary */
+#define LM_NETSTRMS_FILENAME "lmnetstrms"
+
+#endif /* #ifndef INCLUDED_NETSTRMS_H */
diff --git a/runtime/nsd.h b/runtime/nsd.h
new file mode 100644
index 00000000..1b3702a0
--- /dev/null
+++ b/runtime/nsd.h
@@ -0,0 +1,72 @@
+/* The interface definition for "NetStream Drivers" (nsd).
+ *
+ * This is just an abstract driver interface, which needs to be
+ * implemented by concrete classes. As such, no nsd data type itself
+ * is defined.
+ *
+ * Copyright 2008 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.
+ */
+#ifndef INCLUDED_NSD_H
+#define INCLUDED_NSD_H
+
+enum nsdsel_waitOp_e {
+ NSDSEL_RD = 1,
+ NSDSEL_WR = 2,
+ NSDSEL_RDWR = 3
+}; /**< the operation we wait for */
+
+/* nsd_t is actually obj_t (which is somewhat better than void* but in essence
+ * much the same).
+ */
+
+/* interface */
+BEGINinterface(nsd) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(nsd_t **ppThis);
+ rsRetVal (*Destruct)(nsd_t **ppThis);
+ rsRetVal (*Abort)(nsd_t *pThis);
+ rsRetVal (*Rcv)(nsd_t *pThis, uchar *pRcvBuf, ssize_t *pLenBuf);
+ rsRetVal (*Send)(nsd_t *pThis, uchar *pBuf, ssize_t *pLenBuf);
+ rsRetVal (*Connect)(nsd_t *pThis, int family, unsigned char *port, unsigned char *host);
+ rsRetVal (*LstnInit)(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*),
+ uchar *pLstnPort, uchar *pLstnIP, int iSessMax);
+ rsRetVal (*AcceptConnReq)(nsd_t *pThis, nsd_t **ppThis);
+ rsRetVal (*GetRemoteHName)(nsd_t *pThis, uchar **pszName);
+ rsRetVal (*GetRemoteIP)(nsd_t *pThis, uchar **pszIP);
+ rsRetVal (*GetSock)(nsd_t *pThis, int *pSock);
+ rsRetVal (*SetSock)(nsd_t *pThis, int sock);
+ /* GetSock() and SetSock() return an error if the driver does not use plain
+ * OS sockets. This interface is primarily meant as an internal aid for
+ * those drivers that utilize the nsd_ptcp to do some of their work.
+ */
+ENDinterface(nsd)
+#define nsdCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+/* interface for the select call */
+BEGINinterface(nsdsel) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(nsdsel_t **ppThis);
+ rsRetVal (*Destruct)(nsdsel_t **ppThis);
+ rsRetVal (*Add)(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp);
+ rsRetVal (*Select)(nsdsel_t *pNsdsel, int *piNumReady);
+ rsRetVal (*IsReady)(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp, int *pbIsReady);
+ENDinterface(nsdsel)
+#define nsdselCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+#endif /* #ifndef INCLUDED_NSD_H */
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
new file mode 100644
index 00000000..d59043f2
--- /dev/null
+++ b/runtime/nsd_gtls.c
@@ -0,0 +1,444 @@
+/* nsd_gtls.c
+ *
+ * An implementation of the nsd interface for GnuTLS.
+ *
+ * Copyright (C) 2007, 2008 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 <gnutls/gnutls.h>
+
+#include "rsyslog.h"
+#include "syslogd-types.h"
+#include "module-template.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "nsd_ptcp.h"
+#include "nsd_gtls.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(nsd_ptcp)
+
+
+/* a macro to check GnuTLS calls against unexpected errors */
+#define CHKgnutls(x) \
+ if((gnuRet = (x)) != 0) { \
+ dbgprintf("unexpected GnuTLS error %d in %s:%d\n", gnuRet, __FILE__, __LINE__); \
+ gnutls_perror(gnuRet); /* TODO: can we do better? */ \
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR); \
+ }
+
+#define CAFILE "ca.pem" // TODO: allow to specify
+
+/* ------------------------------ GnuTLS specifics ------------------------------ */
+static gnutls_certificate_credentials xcred;
+
+/* globally initialize GnuTLS */
+static rsRetVal
+gtlsGlblInit(void)
+{
+ int gnuRet;
+ DEFiRet;
+
+ CHKgnutls(gnutls_global_init());
+
+ /* X509 stuff */
+ CHKgnutls(gnutls_certificate_allocate_credentials(&xcred));
+
+ /* sets the trusted cas file */
+ gnutls_certificate_set_x509_trust_file(xcred, CAFILE, GNUTLS_X509_FMT_PEM);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* globally de-initialize GnuTLS */
+static rsRetVal
+gtlsGlblExit(void)
+{
+ DEFiRet;
+ /* X509 stuff */
+ gnutls_certificate_free_credentials(xcred);
+ gnutls_global_deinit(); /* we are done... */
+ RETiRet;
+}
+
+
+/* end a GnuTLS session
+ * The function checks if we have a session and ends it only if so. So it can
+ * always be called, even if there currently is no session.
+ */
+static rsRetVal
+gtlsEndSess(nsd_gtls_t *pThis)
+{
+ int gnuRet;
+ DEFiRet;
+
+ if(pThis->bHaveSess) {
+ gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_RDWR);
+ while(gnuRet == GNUTLS_E_INTERRUPTED || gnuRet == GNUTLS_E_AGAIN) {
+ gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_RDWR);
+ }
+ gnutls_deinit(pThis->sess);
+ }
+ RETiRet;
+}
+
+
+/* ---------------------------- end GnuTLS specifics ---------------------------- */
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(nsd_gtls) /* be sure to specify the object type also in END macro! */
+ iRet = nsd_ptcp.Construct(&pThis->pTcp);
+ pThis->iMode = 1; /* TODO: must be made configurable */
+ pThis->iMode = 0; /* TODO: must be made configurable */
+ENDobjConstruct(nsd_gtls)
+
+
+/* destructor for the nsd_gtls object */
+BEGINobjDestruct(nsd_gtls) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(nsd_gtls)
+ if(pThis->iMode == 1) {
+ gtlsEndSess(pThis);
+ }
+
+ if(pThis->pTcp != NULL) {
+ nsd_ptcp.Destruct(&pThis->pTcp);
+ }
+ENDobjDestruct(nsd_gtls)
+
+
+/* Provide access to the underlying OS socket. This is primarily
+ * useful for other drivers (like nsd_gtls) who utilize ourselfs
+ * for some of their functionality. -- rgerhards, 2008-04-18
+ */
+static rsRetVal
+SetSock(nsd_t *pNsd, int sock)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ assert(sock >= 0);
+
+ nsd_ptcp.SetSock(pThis->pTcp, sock);
+
+ RETiRet;
+}
+
+
+/* abort a connection. This is meant to be called immediately
+ * before the Destruct call. -- rgerhards, 2008-03-24
+ */
+static rsRetVal
+Abort(nsd_t *pNsd)
+{
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+
+ if(pThis->iMode == 0) {
+ nsd_ptcp.Abort(pThis->pTcp);
+ }
+
+ RETiRet;
+}
+
+
+
+/* initialize the tcp socket for a listner
+ * Here, we use the ptcp driver - because there is nothing special
+ * at this point with GnuTLS. Things become special once we accept
+ * a session, but not during listener setup.
+ * gerhards, 2008-04-25
+ */
+static rsRetVal
+LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*),
+ uchar *pLstnPort, uchar *pLstnIP, int iSessMax)
+{
+ DEFiRet;
+ iRet = nsd_ptcp.LstnInit(pNS, pUsr, fAddLstn, pLstnPort, pLstnIP, iSessMax);
+ RETiRet;
+}
+
+
+/* get the remote hostname. The returned hostname must be freed by the caller.
+ * rgerhards, 2008-04-25
+ */
+static rsRetVal
+GetRemoteHName(nsd_t *pNsd, uchar **ppszHName)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ iRet = nsd_ptcp.GetRemoteHName(pThis->pTcp, ppszHName);
+ RETiRet;
+}
+
+
+/* get the remote host's IP address. The returned string must be freed by the
+ * caller.
+ * rgerhards, 2008-04-25
+ */
+static rsRetVal
+GetRemoteIP(nsd_t *pNsd, uchar **ppszIP)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ iRet = nsd_ptcp.GetRemoteIP(pThis->pTcp, ppszIP);
+ RETiRet;
+}
+
+
+/* accept an incoming connection request - here, we do the usual accept
+ * handling. TLS specific handling is done thereafter (and if we run in TLS
+ * mode at this time).
+ * rgerhards, 2008-04-25
+ */
+static rsRetVal
+AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew)
+{
+ DEFiRet;
+ nsd_gtls_t *pNew = NULL;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ // TODO: method to construct without pTcp
+ CHKiRet(nsd_gtlsConstruct(&pNew));
+ CHKiRet(nsd_ptcp.Destruct(&pNew->pTcp));
+ CHKiRet(nsd_ptcp.AcceptConnReq(pThis->pTcp, &pNew->pTcp));
+
+ *ppNew = (nsd_t*) pNew;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pNew != NULL)
+ nsd_gtlsDestruct(&pNew);
+ }
+ 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(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ if(pThis->iMode == 0) {
+ CHKiRet(nsd_ptcp.Rcv(pThis->pTcp, pBuf, pLenBuf));
+ FINALIZE;
+ }
+
+ /* in TLS mode now */
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* 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(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf)
+{
+ int iSent;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ if(pThis->iMode == 0) {
+ CHKiRet(nsd_ptcp.Send(pThis->pTcp, pBuf, pLenBuf));
+ FINALIZE;
+ }
+
+ /* in TLS mode now */
+ while(1) { /* loop broken inside */
+ iSent = gnutls_record_send(pThis->sess, pBuf, *pLenBuf);
+ if(iSent >= 0) {
+ *pLenBuf = iSent;
+ break;
+ }
+ if(iSent != GNUTLS_E_INTERRUPTED && iSent != GNUTLS_E_AGAIN)
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* open a connection to a remote host (server). With GnuTLS, we always
+ * open a plain tcp socket and then, if in TLS mode, do a handshake on it.
+ * rgerhards, 2008-03-19
+ */
+static rsRetVal
+Connect(nsd_t *pNsd, int family, uchar *port, uchar *host)
+{
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ int sock;
+ int gnuRet;
+ static const int cert_type_priority[3] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ assert(port != NULL);
+ assert(host != NULL);
+
+ CHKiRet(nsd_ptcp.Connect(pThis->pTcp, family, port, host));
+
+ if(pThis->iMode == 0)
+ FINALIZE;
+
+ /* we reach this point if in TLS mode */
+ CHKgnutls(gnutls_init(&pThis->sess, GNUTLS_CLIENT));
+ pThis->bHaveSess = 1;
+
+ /* Use default priorities */
+ CHKgnutls(gnutls_set_default_priority(pThis->sess));
+ CHKgnutls(gnutls_certificate_type_set_priority(pThis->sess, cert_type_priority));
+
+ /* put the x509 credentials to the current session */
+ CHKgnutls(gnutls_credentials_set(pThis->sess, GNUTLS_CRD_CERTIFICATE, xcred));
+
+ /* assign the socket to GnuTls */
+ CHKiRet(nsd_ptcp.GetSock(pThis->pTcp, &sock));
+ gnutls_transport_set_ptr(pThis->sess, (gnutls_transport_ptr)sock);
+
+ /* and perform the handshake */
+ CHKgnutls(gnutls_handshake(pThis->sess));
+ dbgprintf("GnuTLS handshake succeeded\n");
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->bHaveSess) {
+ gnutls_deinit(pThis->sess);
+ pThis->bHaveSess = 0;
+ }
+ }
+
+ RETiRet;
+}
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(nsd_gtls)
+CODESTARTobjQueryInterface(nsd_gtls)
+ if(pIf->ifVersion != nsdCURR_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 = (rsRetVal(*)(nsd_t**)) nsd_gtlsConstruct;
+ pIf->Destruct = (rsRetVal(*)(nsd_t**)) nsd_gtlsDestruct;
+ pIf->Abort = Abort;
+ pIf->LstnInit = LstnInit;
+ pIf->AcceptConnReq = AcceptConnReq;
+ pIf->Rcv = Rcv;
+ pIf->Send = Send;
+ pIf->Connect = Connect;
+ pIf->SetSock = SetSock;
+ pIf->GetRemoteHName = GetRemoteHName;
+ pIf->GetRemoteIP = GetRemoteIP;
+finalize_it:
+ENDobjQueryInterface(nsd_gtls)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(nsd_gtls, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsd_gtls)
+ gtlsGlblExit(); /* shut down GnuTLS */
+
+ /* release objects we no longer need */
+ objRelease(nsd_ptcp, LM_NSD_PTCP_FILENAME);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ENDObjClassExit(nsd_gtls)
+
+
+/* Initialize the nsd_gtls class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(nsd_gtls, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(nsd_ptcp, LM_NSD_PTCP_FILENAME));
+
+ /* now do global TLS init stuff */
+ CHKiRet(gtlsGlblInit());
+ENDObjClassInit(nsd_gtls)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ nsd_gtlsClassExit();
+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(nsd_gtlsClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h
new file mode 100644
index 00000000..c193f57c
--- /dev/null
+++ b/runtime/nsd_gtls.h
@@ -0,0 +1,49 @@
+/* An implementation of the nsd interface for GnuTLS.
+ *
+ * Copyright 2008 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.
+ */
+
+#ifndef INCLUDED_NSD_GTLS_H
+#define INCLUDED_NSD_GTLS_H
+
+#include "nsd.h"
+
+typedef nsd_if_t nsd_gtls_if_t; /* we just *implement* this interface */
+
+/* the nsd_gtls object */
+struct nsd_gtls_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ nsd_t *pTcp; /**< our aggregated nsd_ptcp data */
+ int iMode; /* 0 - plain tcp, 1 - TLS */
+ gnutls_session sess;
+ int bHaveSess;
+};
+
+/* interface is defined in nsd.h, we just implement it! */
+#define nsd_gtlsCURR_IF_VERSION nsdCURR_IF_VERSION
+
+/* prototypes */
+PROTOTYPEObj(nsd_gtls);
+
+/* the name of our library binary */
+#define LM_NSD_GTLS_FILENAME "lmnsd_gtls"
+
+#endif /* #ifndef INCLUDED_NSD_GTLS_H */
diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c
new file mode 100644
index 00000000..2a74e061
--- /dev/null
+++ b/runtime/nsd_ptcp.c
@@ -0,0 +1,675 @@
+/* nsd_ptcp.c
+ *
+ * An implementation of the nsd interface for plain tcp sockets.
+ *
+ * Copyright 2007, 2008 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 "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <fnmatch.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "syslogd-types.h"
+#include "module-template.h"
+#include "parse.h"
+#include "srUtils.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "net.h"
+#include "netstrms.h"
+#include "netstrm.h"
+#include "nsd_ptcp.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
+
+
+/* a few deinit helpers */
+
+/* close socket if open (may always be called) */
+static void
+sockClose(int *pSock)
+{
+ if(*pSock >= 0) {
+ close(*pSock);
+ *pSock = -1;
+ }
+}
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(nsd_ptcp) /* be sure to specify the object type also in END macro! */
+ pThis->sock = -1;
+ENDobjConstruct(nsd_ptcp)
+
+
+/* destructor for the nsd_ptcp object */
+BEGINobjDestruct(nsd_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(nsd_ptcp)
+ sockClose(&pThis->sock);
+ if(pThis->pRemHostIP != NULL)
+ free(pThis->pRemHostIP);
+ if(pThis->pRemHostName != NULL)
+ free(pThis->pRemHostName);
+ENDobjDestruct(nsd_ptcp)
+
+
+/* Provide access to the underlying OS socket. This is primarily
+ * useful for other drivers (like nsd_gtls) who utilize ourselfs
+ * for some of their functionality. -- rgerhards, 2008-04-18
+ */
+static rsRetVal
+GetSock(nsd_t *pNsd, int *pSock)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert((pThis), nsd_ptcp);
+ assert(pSock != NULL);
+
+ *pSock = pThis->sock;
+
+ RETiRet;
+}
+
+
+/* Provide access to the underlying OS socket. This is primarily
+ * useful for other drivers (like nsd_gtls) who utilize ourselfs
+ * for some of their functionality.
+ * This function sets the socket -- rgerhards, 2008-04-25
+ */
+static rsRetVal
+SetSock(nsd_t *pNsd, int sock)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert((pThis), nsd_ptcp);
+ assert(sock >= 0);
+
+ pThis->sock = sock;
+
+ RETiRet;
+}
+
+
+/* abort a connection. This is meant to be called immediately
+ * before the Destruct call. -- rgerhards, 2008-03-24
+ */
+static rsRetVal
+Abort(nsd_t *pNsd)
+{
+ struct linger ling;
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+
+ DEFiRet;
+ ISOBJ_TYPE_assert((pThis), nsd_ptcp);
+
+ if((pThis)->sock != -1) {
+ ling.l_onoff = 1;
+ ling.l_linger = 0;
+ if(setsockopt((pThis)->sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0 ) {
+ dbgprintf("could not set SO_LINGER, errno %d\n", errno);
+ }
+ }
+
+ RETiRet;
+}
+
+
+/* Set pRemHost based on the address provided. This is to be called upon accept()ing
+ * a connection request. It must be provided by the socket we received the
+ * message on as well as a NI_MAXHOST size large character buffer for the FQDN.
+ * Please see http://www.hmug.org/man/3/getnameinfo.php (under Caveats)
+ * for some explanation of the code found below. If we detect a malicious
+ * hostname, we return RS_RET_MALICIOUS_HNAME and let the caller decide
+ * on how to deal with that.
+ * rgerhards, 2008-03-31
+ */
+static rsRetVal
+FillRemHost(nsd_ptcp_t *pThis, struct sockaddr *pAddr)
+{
+ int error;
+ uchar szIP[NI_MAXHOST] = "";
+ uchar szHname[NI_MAXHOST] = "";
+ struct addrinfo hints, *res;
+ size_t len;
+
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+ assert(pAddr != NULL);
+
+ error = getnameinfo(pAddr, SALEN(pAddr), (char*)szIP, sizeof(szIP), NULL, 0, NI_NUMERICHOST);
+
+ if(error) {
+ dbgprintf("Malformed from address %s\n", gai_strerror(error));
+ strcpy((char*)szHname, "???");
+ strcpy((char*)szIP, "???");
+ ABORT_FINALIZE(RS_RET_INVALID_HNAME);
+ }
+
+ if(!glbl.GetDisableDNS()) {
+ error = getnameinfo(pAddr, SALEN(pAddr), (char*)szHname, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
+ if(error == 0) {
+ memset (&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = SOCK_STREAM;
+ /* we now do a lookup once again. This one should fail,
+ * because we should not have obtained a non-numeric address. If
+ * we got a numeric one, someone messed with DNS!
+ */
+ if(getaddrinfo((char*)szHname, NULL, &hints, &res) == 0) {
+ freeaddrinfo (res);
+ /* OK, we know we have evil, so let's indicate this to our caller */
+ snprintf((char*)szHname, NI_MAXHOST, "[MALICIOUS:IP=%s]", szIP);
+ dbgprintf("Malicious PTR record, IP = \"%s\" HOST = \"%s\"", szIP, szHname);
+ iRet = RS_RET_MALICIOUS_HNAME;
+ }
+ } else {
+ strcpy((char*)szHname, (char*)szIP);
+ }
+ } else {
+ strcpy((char*)szHname, (char*)szIP);
+ }
+
+ /* We now have the names, so now let's allocate memory and store them permanently.
+ * (side note: we may hold on to these values for quite a while, thus we trim their
+ * memory consumption)
+ */
+ len = strlen((char*)szIP) + 1; /* +1 for \0 byte */
+ if((pThis->pRemHostIP = malloc(len)) == NULL)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ memcpy(pThis->pRemHostIP, szIP, len);
+
+ len = strlen((char*)szHname) + 1; /* +1 for \0 byte */
+ if((pThis->pRemHostName = malloc(len)) == NULL) {
+ free(pThis->pRemHostIP); /* prevent leak */
+ pThis->pRemHostIP = NULL;
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ memcpy(pThis->pRemHostName, szHname, len);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* accept an incoming connection request
+ * rgerhards, 2008-04-22
+ */
+static rsRetVal
+AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew)
+{
+ int sockflags;
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ nsd_ptcp_t *pNew = NULL;
+ int iNewSock = -1;
+
+ DEFiRet;
+ assert(ppNew != NULL);
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+
+ iNewSock = accept(pThis->sock, (struct sockaddr*) &addr, &addrlen);
+ if(iNewSock < 0) {
+ ABORT_FINALIZE(RS_RET_ACCEPT_ERR);
+ }
+
+ /* construct our object so that we can use it... */
+ CHKiRet(nsd_ptcpConstruct(&pNew));
+
+ CHKiRet(FillRemHost(pNew, (struct sockaddr*) &addr));
+
+ /* set the new socket to non-blocking IO -TODO:do we really need to do this here? Do we always want it? */
+ if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) {
+ sockflags |= O_NONBLOCK;
+ /* SETFL could fail too, so get it caught by the subsequent
+ * error check.
+ */
+ sockflags = fcntl(iNewSock, F_SETFL, sockflags);
+ }
+ if(sockflags == -1) {
+ dbgprintf("error %d setting fcntl(O_NONBLOCK) on tcp socket %d", errno, iNewSock);
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+ pNew->sock = iNewSock;
+ *ppNew = (nsd_t*) pNew;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pNew != NULL)
+ nsd_ptcpDestruct(&pNew);
+ /* the close may be redundant, but that doesn't hurt... */
+ sockClose(&iNewSock);
+ }
+
+ RETiRet;
+}
+
+
+/* initialize tcp sockets for a listner. The initialized sockets are passed to the
+ * app-level caller via a callback.
+ * pLstnPort must point to a port name or number. NULL is NOT permitted. pLstnIP
+ * points to the port to listen to (NULL means "all"), iMaxSess has the maximum
+ * number of sessions 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;
+ netstrm_t *pNewStrm = NULL;
+ nsd_t *pNewNsd = NULL;
+ int error, maxs, on = 1;
+ int sock;
+ int numSocks;
+ int sockflags;
+ struct addrinfo hints, *res = NULL, *r;
+
+ ISOBJ_TYPE_assert(pNS, netstrms);
+ assert(fAddLstn != NULL);
+ assert(pLstnPort != NULL);
+ assert(iSessMax >= 0);
+
+ dbgprintf("creating tcp listen socket on port %s\n", pLstnPort);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = glbl.GetDefPFFamily();
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo((char*)pLstnIP, (char*) pLstnPort, &hints, &res);
+ if(error) {
+ dbgprintf("error %d querying port '%s'\n", error, pLstnPort);
+ ABORT_FINALIZE(RS_RET_INVALID_PORT);
+ }
+
+ /* Count max number of sockets we may open */
+ for(maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++)
+ /* EMPTY */;
+
+ numSocks = 0; /* num of sockets counter at start of array */
+ for(r = res; r != NULL ; r = r->ai_next) {
+ sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if(sock < 0) {
+ if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT))
+ dbgprintf("error %d creating tcp listen socket", errno);
+ /* 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(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&iOn, sizeof (iOn)) < 0) {
+ close(sock);
+ continue;
+ }
+ }
+#endif
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) {
+ dbgprintf("error %d setting tcp socket option\n", errno);
+ close(sock);
+ continue;
+ }
+
+ /* We use non-blocking IO! */
+ if((sockflags = fcntl(sock, F_GETFL)) != -1) {
+ sockflags |= O_NONBLOCK;
+ /* SETFL could fail too, so get it caught by the subsequent
+ * error check.
+ */
+ sockflags = fcntl(sock, F_SETFL, sockflags);
+ }
+ if(sockflags == -1) {
+ dbgprintf("error %d setting fcntl(O_NONBLOCK) on tcp socket", errno);
+ close(sock);
+ continue;
+ }
+
+
+
+ /* We need to enable BSD compatibility. Otherwise an attacker
+ * could flood our log files by sending us tons of ICMP errors.
+ */
+#ifndef BSD
+ if(net.should_use_so_bsdcompat()) {
+ if (setsockopt(sock, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *) &on, sizeof(on)) < 0) {
+ errmsg.LogError(NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
+ close(sock);
+ continue;
+ }
+ }
+#endif
+
+ if( (bind(sock, r->ai_addr, r->ai_addrlen) < 0)
+#ifndef IPV6_V6ONLY
+ && (errno != EADDRINUSE)
+#endif
+ ) {
+ /* TODO: check if *we* bound the socket - else we *have* an error! */
+ dbgprintf("error %d while binding tcp socket", errno);
+ close(sock);
+ continue;
+ }
+
+ if(listen(sock, 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.
+ */
+ dbgprintf("listen with a backlog of %d failed - retrying with default of 32.",
+ iSessMax / 10 + 5);
+ if(listen(sock, 32) < 0) {
+ dbgprintf("tcp listen error %d, suspending\n", errno);
+ close(sock);
+ continue;
+ }
+ }
+
+ /* if we reach this point, we were able to obtain a valid socket, so we can
+ * construct a new netstrm obj and hand it over to the upper layers for inclusion
+ * into their socket array. -- rgerhards, 2008-04-23
+ */
+RUNLOG_VAR("%d", sock);
+ CHKiRet(pNS->Drvr.Construct(&pNewNsd));
+ CHKiRet(pNS->Drvr.SetSock(pNewNsd, sock));
+RUNLOG;
+ CHKiRet(netstrms.CreateStrm(pNS, &pNewStrm));
+ pNewStrm->pDrvrData = (nsd_t*) pNewNsd;
+RUNLOG;
+ CHKiRet(fAddLstn(pUsr, pNewStrm));
+ pNewNsd = NULL;
+ pNewStrm = NULL;
+ ++numSocks;
+ }
+
+ if(numSocks != maxs)
+ dbgprintf("We could initialize %d TCP listen sockets out of %d we received "
+ "- this may or may not be an error indication.\n", numSocks, maxs);
+
+ if(numSocks == 0) {
+ dbgprintf("No TCP listen sockets could successfully be initialized");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_BIND);
+ }
+
+finalize_it:
+ if(res != NULL)
+ freeaddrinfo(res);
+
+ if(iRet != RS_RET_OK) {
+ if(pNewStrm != NULL)
+ netstrm.Destruct(&pNewStrm);
+ if(pNewNsd != NULL)
+ pNS->Drvr.Destruct(&pNewNsd);
+ }
+
+ 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(nsd_t *pNsd, uchar *pRcvBuf, ssize_t *pLenBuf)
+{
+ DEFiRet;
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+
+ *pLenBuf = recv(pThis->sock, pRcvBuf, *pLenBuf, MSG_DONTWAIT);
+
+ RETiRet;
+}
+
+
+/* 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(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ ssize_t written;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+
+ written = send(pThis->sock, pBuf, *pLenBuf, 0);
+
+ if(written == -1) {
+ switch(errno) {
+ case EAGAIN:
+ case EINTR:
+ /* this is fine, just retry... */
+ written = 0;
+ break;
+ default:
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ break;
+ }
+ }
+
+ *pLenBuf = written;
+finalize_it:
+ RETiRet;
+}
+
+
+/* open a connection to a remote host (server).
+ * rgerhards, 2008-03-19
+ */
+static rsRetVal
+Connect(nsd_t *pNsd, int family, uchar *port, uchar *host)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ struct addrinfo *res = NULL;
+ struct addrinfo hints;
+
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+ assert(port != NULL);
+ assert(host != NULL);
+ assert(pThis->sock == -1);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ if(getaddrinfo((char*)host, (char*)port, &hints, &res) != 0) {
+ dbgprintf("error %d in getaddrinfo\n", errno);
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+ if((pThis->sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+ if(connect(pThis->sock, res->ai_addr, res->ai_addrlen) != 0) {
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+finalize_it:
+ if(res != NULL)
+ freeaddrinfo(res);
+
+ if(iRet != RS_RET_OK) {
+ sockClose(&pThis->sock);
+ }
+
+ RETiRet;
+}
+
+
+/* get the remote hostname. The returned hostname must be freed by the
+ * caller.
+ * rgerhards, 2008-04-24
+ */
+static rsRetVal
+GetRemoteHName(nsd_t *pNsd, uchar **ppszHName)
+{
+ DEFiRet;
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+ assert(ppszHName != NULL);
+
+ // TODO: how can the RemHost be empty?
+ CHKmalloc(*ppszHName = (uchar*)strdup(pThis->pRemHostName == NULL ? "" : (char*) pThis->pRemHostName));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get the remote host's IP address. The returned string must be freed by the
+ * caller.
+ * rgerhards, 2008-04-24
+ */
+static rsRetVal
+GetRemoteIP(nsd_t *pNsd, uchar **ppszIP)
+{
+ DEFiRet;
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+ assert(ppszIP != NULL);
+
+ CHKmalloc(*ppszIP = (uchar*)strdup(pThis->pRemHostIP == NULL ? "" : (char*) pThis->pRemHostIP));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(nsd_ptcp)
+CODESTARTobjQueryInterface(nsd_ptcp)
+ if(pIf->ifVersion != nsdCURR_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 = (rsRetVal(*)(nsd_t**)) nsd_ptcpConstruct;
+ pIf->Destruct = (rsRetVal(*)(nsd_t**)) nsd_ptcpDestruct;
+ pIf->Abort = Abort;
+ pIf->GetSock = GetSock;
+ pIf->SetSock = SetSock;
+ pIf->Rcv = Rcv;
+ pIf->Send = Send;
+ pIf->LstnInit = LstnInit;
+ pIf->AcceptConnReq = AcceptConnReq;
+ pIf->Connect = Connect;
+ pIf->GetRemoteHName = GetRemoteHName;
+ pIf->GetRemoteIP = GetRemoteIP;
+finalize_it:
+ENDobjQueryInterface(nsd_ptcp)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(nsd_ptcp, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsd_ptcp)
+ /* release objects we no longer need */
+ objRelease(net, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrm, LM_NETSTRM_FILENAME);
+ objRelease(netstrms, LM_NETSTRMS_FILENAME);
+ENDObjClassExit(nsd_ptcp)
+
+
+/* Initialize the nsd_ptcp class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(nsd_ptcp, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(net, CORE_COMPONENT));
+ CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+
+ /* set our own handlers */
+ENDObjClassInit(nsd_ptcp)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ nsd_ptcpClassExit();
+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(nsd_ptcpClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/nsd_ptcp.h b/runtime/nsd_ptcp.h
new file mode 100644
index 00000000..efd3ed05
--- /dev/null
+++ b/runtime/nsd_ptcp.h
@@ -0,0 +1,47 @@
+/* An implementation of the nsd interface for plain tcp sockets.
+ *
+ * Copyright 2007, 2008 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.
+ */
+
+#ifndef INCLUDED_NSD_PTCP_H
+#define INCLUDED_NSD_PTCP_H
+
+#include "nsd.h"
+typedef nsd_if_t nsd_ptcp_if_t; /* we just *implement* this interface */
+
+/* the nsd_ptcp object */
+struct nsd_ptcp_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ uchar *pRemHostIP; /**< IP address of remote peer (currently used in server mode, only) */
+ uchar *pRemHostName; /**< host name of remote peer (currently used in server mode, only) */
+ int sock; /**< the socket we use for regular, single-socket, operations */
+};
+
+/* interface is defined in nsd.h, we just implement it! */
+#define nsd_ptcpCURR_IF_VERSION nsdCURR_IF_VERSION
+
+/* prototypes */
+PROTOTYPEObj(nsd_ptcp);
+
+/* the name of our library binary */
+#define LM_NSD_PTCP_FILENAME "lmnsd_ptcp"
+
+#endif /* #ifndef INCLUDED_NSD_PTCP_H */
diff --git a/runtime/nsdsel_gtls.c b/runtime/nsdsel_gtls.c
new file mode 100644
index 00000000..7cafec49
--- /dev/null
+++ b/runtime/nsdsel_gtls.c
@@ -0,0 +1,184 @@
+/* nsdsel_gtls.c
+ *
+ * An implementation of the nsd select() interface for GnuTLS.
+ *
+ * Copyright (C) 2008 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 <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <gnutls/gnutls.h>
+
+#include "rsyslog.h"
+#include "module-template.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "nsd.h"
+#include "nsd_gtls.h"
+#include "nsdsel_ptcp.h"
+#include "nsdsel_gtls.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(nsdsel_ptcp)
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(nsdsel_gtls) /* be sure to specify the object type also in END macro! */
+ iRet = nsdsel_ptcp.Construct(&pThis->pTcp);
+ENDobjConstruct(nsdsel_gtls)
+
+
+/* destructor for the nsdsel_gtls object */
+BEGINobjDestruct(nsdsel_gtls) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(nsdsel_gtls)
+ if(pThis->pTcp != NULL)
+ nsdsel_ptcp.Destruct(&pThis->pTcp);
+ENDobjDestruct(nsdsel_gtls)
+
+
+/* Add a socket to the select set */
+static rsRetVal
+Add(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp)
+{
+ DEFiRet;
+ nsdsel_gtls_t *pThis = (nsdsel_gtls_t*) pNsdsel;
+ nsd_gtls_t *pNsdGTLS = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert(pThis, nsdsel_gtls);
+ ISOBJ_TYPE_assert(pNsdGTLS, nsd_gtls);
+ iRet = nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, waitOp);
+ RETiRet;
+}
+
+
+/* perform the select() piNumReady returns how many descriptors are ready for IO
+ * TODO: add timeout!
+ */
+static rsRetVal
+Select(nsdsel_t *pNsdsel, int *piNumReady)
+{
+ DEFiRet;
+ nsdsel_gtls_t *pThis = (nsdsel_gtls_t*) pNsdsel;
+
+ ISOBJ_TYPE_assert(pThis, nsdsel_gtls);
+ iRet = nsdsel_ptcp.Select(pThis->pTcp, piNumReady);
+ RETiRet;
+}
+
+
+/* check if a socket is ready for IO */
+static rsRetVal
+IsReady(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp, int *pbIsReady)
+{
+ DEFiRet;
+ nsdsel_gtls_t *pThis = (nsdsel_gtls_t*) pNsdsel;
+ nsd_gtls_t *pNsdGTLS = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert(pThis, nsdsel_gtls);
+ ISOBJ_TYPE_assert(pNsdGTLS, nsd_gtls);
+ iRet = nsdsel_ptcp.IsReady(pThis->pTcp, pNsdGTLS->pTcp, waitOp, pbIsReady);
+ RETiRet;
+}
+
+
+/* ------------------------------ end support for the select() interface ------------------------------ */
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(nsdsel_gtls)
+CODESTARTobjQueryInterface(nsdsel_gtls)
+ if(pIf->ifVersion != nsdCURR_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 = (rsRetVal(*)(nsdsel_t**)) nsdsel_gtlsConstruct;
+ pIf->Destruct = (rsRetVal(*)(nsdsel_t**)) nsdsel_gtlsDestruct;
+ pIf->Add = Add;
+ pIf->Select = Select;
+ pIf->IsReady = IsReady;
+finalize_it:
+ENDobjQueryInterface(nsdsel_gtls)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(nsdsel_gtls, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsdsel_gtls)
+ /* release objects we no longer need */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(nsdsel_ptcp, LM_NSDSEL_PTCP_FILENAME);
+ENDObjClassExit(nsdsel_gtls)
+
+
+/* Initialize the nsdsel_gtls class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(nsdsel_gtls, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(nsdsel_ptcp, LM_NSDSEL_PTCP_FILENAME));
+
+ /* set our own handlers */
+ENDObjClassInit(nsdsel_gtls)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ nsdsel_gtlsClassExit();
+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(nsdsel_gtlsClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/nsdsel_gtls.h b/runtime/nsdsel_gtls.h
new file mode 100644
index 00000000..a309d302
--- /dev/null
+++ b/runtime/nsdsel_gtls.h
@@ -0,0 +1,45 @@
+/* An implementation of the nsd select interface for GnuTLS.
+ *
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef INCLUDED_NSDSEL_GTLS_H
+#define INCLUDED_NSDSEL_GTLS_H
+
+#include "nsd.h"
+typedef nsdsel_if_t nsdsel_gtls_if_t; /* we just *implement* this interface */
+
+/* the nsdsel_gtls object */
+struct nsdsel_gtls_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ nsdsel_t *pTcp; /* our aggregated ptcp sel handler (which does almost everything) */
+};
+
+/* interface is defined in nsd.h, we just implement it! */
+#define nsdsel_gtlsCURR_IF_VERSION nsdCURR_IF_VERSION
+
+/* prototypes */
+PROTOTYPEObj(nsdsel_gtls);
+
+/* the name of our library binary */
+#define LM_NSDSEL_GTLS_FILENAME "lmnsdsel_gtls"
+
+#endif /* #ifndef INCLUDED_NSDSEL_GTLS_H */
diff --git a/runtime/nsdsel_ptcp.c b/runtime/nsdsel_ptcp.c
new file mode 100644
index 00000000..b439063a
--- /dev/null
+++ b/runtime/nsdsel_ptcp.c
@@ -0,0 +1,222 @@
+/* nsdsel_ptcp.c
+ *
+ * An implementation of the nsd select() interface for plain tcp sockets.
+ *
+ * Copyright 2008 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 <errno.h>
+#include <string.h>
+#include <sys/select.h>
+
+#include "rsyslog.h"
+#include "module-template.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "nsd_ptcp.h"
+#include "nsdsel_ptcp.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(nsdsel_ptcp) /* be sure to specify the object type also in END macro! */
+ pThis->maxfds = 0;
+ FD_ZERO(&pThis->readfds);
+ FD_ZERO(&pThis->writefds);
+ENDobjConstruct(nsdsel_ptcp)
+
+
+/* destructor for the nsdsel_ptcp object */
+BEGINobjDestruct(nsdsel_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(nsdsel_ptcp)
+ENDobjDestruct(nsdsel_ptcp)
+
+
+/* Add a socket to the select set */
+static rsRetVal
+Add(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp)
+{
+ DEFiRet;
+ nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel;
+ nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd;
+
+ ISOBJ_TYPE_assert(pSock, nsd_ptcp);
+ ISOBJ_TYPE_assert(pThis, nsdsel_ptcp);
+
+ switch(waitOp) {
+ case NSDSEL_RD:
+ FD_SET(pSock->sock, &pThis->readfds);
+ break;
+ case NSDSEL_WR:
+ FD_SET(pSock->sock, &pThis->writefds);
+ break;
+ case NSDSEL_RDWR:
+ FD_SET(pSock->sock, &pThis->readfds);
+ FD_SET(pSock->sock, &pThis->writefds);
+ break;
+ }
+
+ if(pSock->sock > pThis->maxfds)
+ pThis->maxfds = pSock->sock;
+
+ RETiRet;
+}
+
+
+/* perform the select() piNumReady returns how many descriptors are ready for IO
+ * TODO: add timeout!
+ */
+static rsRetVal
+Select(nsdsel_t *pNsdsel, int *piNumReady)
+{
+ DEFiRet;
+ int i;
+ nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel;
+
+ ISOBJ_TYPE_assert(pThis, nsdsel_ptcp);
+ assert(piNumReady != NULL);
+
+ if(Debug) { // TODO: debug setting!
+ // TODO: name in dbgprintf!
+ dbgprintf("--------<NSDSEL_PTCP> calling select, active fds (max %d): ", pThis->maxfds);
+ for(i = 0; i <= pThis->maxfds; ++i)
+ if(FD_ISSET(i, &pThis->readfds) || FD_ISSET(i, &pThis->writefds))
+ dbgprintf("%d ", i);
+ dbgprintf("\n");
+ }
+
+ /* now do the select */
+ *piNumReady = select(pThis->maxfds+1, &pThis->readfds, &pThis->writefds, NULL, NULL);
+
+ RETiRet;
+}
+
+
+/* check if a socket is ready for IO */
+static rsRetVal
+IsReady(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp, int *pbIsReady)
+{
+ DEFiRet;
+ nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel;
+ nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd;
+
+ ISOBJ_TYPE_assert(pThis, nsdsel_ptcp);
+ ISOBJ_TYPE_assert(pSock, nsd_ptcp);
+ assert(pbIsReady != NULL);
+
+ switch(waitOp) {
+ case NSDSEL_RD:
+ *pbIsReady = FD_ISSET(pSock->sock, &pThis->readfds);
+ break;
+ case NSDSEL_WR:
+ *pbIsReady = FD_ISSET(pSock->sock, &pThis->writefds);
+ break;
+ case NSDSEL_RDWR:
+ *pbIsReady = FD_ISSET(pSock->sock, &pThis->readfds)
+ | FD_ISSET(pSock->sock, &pThis->writefds);
+ break;
+ }
+
+ RETiRet;
+}
+
+
+/* ------------------------------ end support for the select() interface ------------------------------ */
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(nsdsel_ptcp)
+CODESTARTobjQueryInterface(nsdsel_ptcp)
+ if(pIf->ifVersion != nsdCURR_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 = (rsRetVal(*)(nsdsel_t**)) nsdsel_ptcpConstruct;
+ pIf->Destruct = (rsRetVal(*)(nsdsel_t**)) nsdsel_ptcpDestruct;
+ pIf->Add = Add;
+ pIf->Select = Select;
+ pIf->IsReady = IsReady;
+finalize_it:
+ENDobjQueryInterface(nsdsel_ptcp)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(nsdsel_ptcp, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsdsel_ptcp)
+ /* release objects we no longer need */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ENDObjClassExit(nsdsel_ptcp)
+
+
+/* Initialize the nsdsel_ptcp class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(nsdsel_ptcp, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+
+ /* set our own handlers */
+ENDObjClassInit(nsdsel_ptcp)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ nsdsel_ptcpClassExit();
+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(nsdsel_ptcpClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/nsdsel_ptcp.h b/runtime/nsdsel_ptcp.h
new file mode 100644
index 00000000..93874e55
--- /dev/null
+++ b/runtime/nsdsel_ptcp.h
@@ -0,0 +1,47 @@
+/* An implementation of the nsd select interface for plain tcp sockets.
+ *
+ * Copyright 2008 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.
+ */
+
+#ifndef INCLUDED_NSDSEL_PTCP_H
+#define INCLUDED_NSDSEL_PTCP_H
+
+#include "nsd.h"
+typedef nsdsel_if_t nsdsel_ptcp_if_t; /* we just *implement* this interface */
+
+/* the nsdsel_ptcp object */
+struct nsdsel_ptcp_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ int maxfds;
+ fd_set readfds;
+ fd_set writefds;
+};
+
+/* interface is defined in nsd.h, we just implement it! */
+#define nsdsel_ptcpCURR_IF_VERSION nsdCURR_IF_VERSION
+
+/* prototypes */
+PROTOTYPEObj(nsdsel_ptcp);
+
+/* the name of our library binary */
+#define LM_NSDSEL_PTCP_FILENAME "lmnsdsel_ptcp"
+
+#endif /* #ifndef INCLUDED_NSDSEL_PTCP_H */
diff --git a/runtime/nssel.c b/runtime/nssel.c
new file mode 100644
index 00000000..5333fc75
--- /dev/null
+++ b/runtime/nssel.c
@@ -0,0 +1,229 @@
+/* nssel.c
+ *
+ * The io waiter is a helper object enabling us to wait on a set of streams to become
+ * ready for IO - this is modelled after select(). We need this, because
+ * stream drivers may have different concepts. Consequently,
+ * the structure must contain nsd_t's from the same stream driver type
+ * only. This is implemented as a singly-linked list where every
+ * new element is added at the top of the list.
+ *
+ * Work on this module begun 2008-04-22 by Rainer Gerhards.
+ *
+ * Copyright 2008 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 "rsyslog.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "module-template.h"
+#include "netstrm.h"
+#include "nssel.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
+
+
+/* load our low-level driver. This must be done before any
+ * driver-specific functions (allmost all...) can be carried
+ * out. Note that the driver's .ifIsLoaded is correctly
+ * initialized by calloc() and we depend on that.
+ * rgerhards, 2008-04-18
+ */
+static rsRetVal
+loadDrvr(nssel_t *pThis)
+{
+ uchar *pDrvrName;
+ DEFiRet;
+
+ pDrvrName = pThis->pDrvrName;
+ if(pDrvrName == NULL) /* if no drvr name is set, use system default */
+ pDrvrName = glbl.GetDfltNetstrmDrvr();
+
+ pThis->Drvr.ifVersion = nsdCURR_IF_VERSION;
+ /* The pDrvrName+2 below is a hack to obtain the object name. It
+ * safes us to have yet another variable with the name without "lm" in
+ * front of it. If we change the module load interface, we may re-think
+ * about this hack, but for the time being it is efficient and clean
+ * enough. -- rgerhards, 2008-04-18
+ */
+ //CHKiRet(obj.UseObj(__FILE__, pDrvrName+2, pDrvrName, (void*) &pThis->Drvr));
+ CHKiRet(obj.UseObj(__FILE__, "nsdsel_gtls", "lmnsdsel_gtls", (void*) &pThis->Drvr));
+finalize_it:
+ RETiRet;
+}
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(nssel) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(nssel)
+
+
+/* destructor for the nssel object */
+BEGINobjDestruct(nssel) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(nssel)
+ if(pThis->pDrvrData != NULL)
+ pThis->Drvr.Destruct(&pThis->pDrvrData);
+ENDobjDestruct(nssel)
+
+
+/* ConstructionFinalizer */
+static rsRetVal
+ConstructFinalize(nssel_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nssel);
+ CHKiRet(loadDrvr(pThis));
+ CHKiRet(pThis->Drvr.Construct(&pThis->pDrvrData));
+finalize_it:
+ RETiRet;
+}
+
+
+/* Add a stream object to the current select() set.
+ * Note that a single stream may have multiple "sockets" if
+ * it is a listener. If so, all of them are begin added.
+ */
+static rsRetVal
+Add(nssel_t *pThis, netstrm_t *pStrm, nsdsel_waitOp_t waitOp)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nssel);
+ ISOBJ_TYPE_assert(pStrm, netstrm);
+
+ CHKiRet(pThis->Drvr.Add(pThis->pDrvrData, pStrm->pDrvrData, waitOp));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* wait for IO to happen on one of our netstreams. iNumReady has
+ * the number of ready "sockets" after the call. This function blocks
+ * until some are ready. EAGAIN is retried.
+ */
+static rsRetVal
+Wait(nssel_t *pThis, int *piNumReady)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nssel);
+ assert(piNumReady != NULL);
+ iRet = pThis->Drvr.Select(pThis->pDrvrData, piNumReady);
+ RETiRet;
+}
+
+
+/* Check if a stream is ready for IO. *piNumReady contains the remaining number
+ * of ready streams. Note that this function may say the stream is not ready
+ * but still decrement *piNumReady. This can happen when (e.g. with TLS) the low
+ * level driver requires some IO which is hidden from the upper layer point of view.
+ * rgerhards, 2008-04-23
+ */
+static rsRetVal
+IsReady(nssel_t *pThis, netstrm_t *pStrm, nsdsel_waitOp_t waitOp, int *pbIsReady, int *piNumReady)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nssel);
+ ISOBJ_TYPE_assert(pStrm, netstrm);
+ assert(pbIsReady != NULL);
+ assert(piNumReady != NULL);
+ iRet = pThis->Drvr.IsReady(pThis->pDrvrData, pStrm->pDrvrData, waitOp, pbIsReady);
+ RETiRet;
+}
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(nssel)
+CODESTARTobjQueryInterface(nssel)
+ if(pIf->ifVersion != nsselCURR_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 = nsselConstruct;
+ pIf->ConstructFinalize = ConstructFinalize;
+ pIf->Destruct = nsselDestruct;
+ pIf->Add = Add;
+ pIf->Wait = Wait;
+ pIf->IsReady = IsReady;
+finalize_it:
+ENDobjQueryInterface(nssel)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(nssel, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nssel)
+ /* release objects we no longer need */
+ objRelease(glbl, CORE_COMPONENT);
+ENDObjClassExit(nssel)
+
+
+/* Initialize the nssel class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(nssel, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+
+ /* set our own handlers */
+ENDObjClassInit(nssel)
+
+
+/* --------------- here now comes the plumbing that makes us a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ nsselClassExit();
+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(nsselClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/nssel.h b/runtime/nssel.h
new file mode 100644
index 00000000..2f907caa
--- /dev/null
+++ b/runtime/nssel.h
@@ -0,0 +1,55 @@
+/* Definitions for the nssel IO waiter.
+ *
+ * Copyright 2008 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.
+ */
+
+#ifndef INCLUDED_NSSEL_H
+#define INCLUDED_NSSEL_H
+
+#include "nsd.h"
+
+/* the nssel object */
+struct nssel_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ nsd_t *pDrvrData; /**< the driver's data elements */
+ uchar *pDrvrName; /**< nsd driver name to use, or NULL if system default */
+ nsdsel_if_t Drvr; /**< our stream driver */
+};
+
+
+/* interface */
+BEGINinterface(nssel) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(nssel_t **ppThis);
+ rsRetVal (*ConstructFinalize)(nssel_t *pThis);
+ rsRetVal (*Destruct)(nssel_t **ppThis);
+ rsRetVal (*Add)(nssel_t *pThis, netstrm_t *pStrm, nsdsel_waitOp_t waitOp);
+ rsRetVal (*Wait)(nssel_t *pThis, int *pNumReady);
+ rsRetVal (*IsReady)(nssel_t *pThis, netstrm_t *pStrm, nsdsel_waitOp_t waitOp, int *pbIsReady, int *piNumReady);
+ENDinterface(nssel)
+#define nsselCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+/* prototypes */
+PROTOTYPEObj(nssel);
+
+/* the name of our library binary */
+#define LM_NSSEL_FILENAME "lmnssel"
+
+#endif /* #ifndef INCLUDED_NSSEL_H */
diff --git a/runtime/obj-types.h b/runtime/obj-types.h
index e245b633..5f531eb1 100644
--- a/runtime/obj-types.h
+++ b/runtime/obj-types.h
@@ -81,13 +81,13 @@ struct objInfo_s {
};
-typedef struct obj { /* the dummy struct that each derived class can be casted to */
+struct obj_s { /* the dummy struct that each derived class can be casted to */
objInfo_t *pObjInfo;
#ifndef NDEBUG /* this means if debug... */
unsigned int iObjCooCKiE; /* must always be 0xBADEFEE for a valid object */
#endif
uchar *pszName; /* the name of *this* specific object instance */
-} obj_t;
+};
/* macros which must be gloablly-visible (because they are used during definition of
@@ -106,8 +106,12 @@ typedef struct obj { /* the dummy struct that each derived class can be casted t
do { \
ASSERT(pObj != NULL); \
ASSERT((unsigned) ((obj_t*) (pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \
- ASSERT(!strcmp((char*)(((obj_t*)pObj)->pObjInfo->pszID), #objType)); \
- } while(0);
+ if(strcmp((char*)(((obj_t*)pObj)->pObjInfo->pszID), #objType)) { \
+ dbgprintf("%s:%d ISOBJ assert failure: invalid object type, expected '%s' " \
+ "actual '%s'\n", __FILE__, __LINE__, #objType, (((obj_t*)pObj)->pObjInfo->pszID)); \
+ assert(0); /* trigger assertion, messge we already have */ \
+ } \
+ } while(0)
#else /* non-debug mode, no checks but much faster */
# define BEGINobjInstance obj_t objData
# define ISOBJ_TYPE_assert(pObj, objType)
@@ -377,9 +381,6 @@ rsRetVal objName##ClassExit(void) \
*/
#define CORE_COMPONENT NULL /* use this to indicate this is a core component */
#define DONT_LOAD_LIB NULL /* do not load a library to obtain object interface (currently same as CORE_COMPONENT) */
-/*#define objUse(objName, MYLIB, FILENAME) \
- obj.UseObj(__FILE__, (uchar*)#objName, MYLIB, (uchar*)FILENAME, (void*) &objName)
-*/
#define objUse(objName, FILENAME) \
obj.UseObj(__FILE__, (uchar*)#objName, (uchar*)FILENAME, (void*) &objName)
#define objRelease(objName, FILENAME) \
diff --git a/runtime/obj.c b/runtime/obj.c
index 8ab606f9..18a4a726 100644
--- a/runtime/obj.c
+++ b/runtime/obj.c
@@ -1198,7 +1198,7 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
FINALIZE; /* if it is not a lodable module, we do not need to do anything... */
if(pIf->ifIsLoaded == 0) {
- ABORT_FINALIZE(RS_RET_OK); /* we are already set */ /* TODO: flag an error? */
+ ABORT_FINALIZE(RS_RET_OK); /* we are not loaded - this is perfectly OK... */
}
if(pIf->ifIsLoaded == 2) {
pIf->ifIsLoaded = 0; /* clean up */
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 89ae1e66..19fda468 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -61,13 +61,27 @@
/* define some base data types */
typedef unsigned char uchar;/* get rid of the unhandy "unsigned char" */
typedef struct thrdInfo thrdInfo_t;
-typedef struct filed selector_t; /* TODO: this so far resides in syslogd.c, think about modularization */
+typedef struct obj_s obj_t;
+typedef struct filed selector_t;/* TODO: this so far resides in syslogd.c, think about modularization */
typedef struct NetAddr netAddr_t;
+typedef struct netstrms_s netstrms_t;
+typedef struct netstrm_s netstrm_t;
+typedef struct nssel_s nssel_t;
+typedef enum nsdsel_waitOp_e nsdsel_waitOp_t;
+typedef struct nsd_ptcp_s nsd_ptcp_t;
+typedef struct nsd_gtls_s nsd_gtls_t;
+typedef struct nsd_gsspi_s nsd_gsspi_t;
+typedef struct nsd_nss_s nsd_nss_t;
+typedef struct nsdsel_ptcp_s nsdsel_ptcp_t;
+typedef struct nsdsel_gtls_s nsdsel_gtls_t;
+typedef obj_t nsd_t;
+typedef obj_t nsdsel_t;
typedef struct msg msg_t;
typedef struct interface_s interface_t;
typedef struct objInfo_s objInfo_t;
typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */
typedef rsRetVal (*errLogFunc_t)(uchar*); /* this is a trick to store a function ptr to a function returning a function ptr... */
+typedef struct tcpsrv_s tcpsrv_t;
/* some universal 64 bit define... */
typedef long long int64;
@@ -197,6 +211,14 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_MAIL_NO_TO = -2071, /**< recipient for mail destination is missing */
RS_RET_MAIL_NO_FROM = -2072, /**< sender for mail destination is missing */
RS_RET_INVALID_PRI = -2073, /**< PRI value is invalid */
+ RS_RET_MALICIOUS_HNAME = -2074, /**< remote peer is trying malicious things with its hostname */
+ RS_RET_ACCEPT_ERR = -2074, /**< error during accept() system call */
+ RS_RET_INVALID_HNAME = -2075, /**< remote peer's hostname invalid or unobtainable */
+ RS_RET_INVALID_PORT = -2076, /**< invalid port value */
+ RS_RET_COULD_NOT_BIND = -2077, /**< could not bind socket, defunct */
+ RS_RET_GNUTLS_ERR = -2078, /**< (unexpected) error in GnuTLS call */
+ RS_RET_MAX_SESS_REACHED = -2079, /**< max nbr of sessions reached, can not create more */
+ RS_RET_MAX_LSTN_REACHED = -2080, /**< max nbr of listeners reached, can not create more */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/tcpclt.c b/tcpclt.c
index 7216caae..2bc2ea56 100644
--- a/tcpclt.c
+++ b/tcpclt.c
@@ -51,7 +51,6 @@ MODULE_TYPE_LIB
DEFobjStaticHelpers
/* Initialize TCP sockets (for sender)
- * This is done once per selector line, if not yet initialized.
*/
static int
CreateSocket(struct addrinfo *addrDest)
diff --git a/tcpclt.h b/tcpclt.h
index 15344266..b7aada65 100644
--- a/tcpclt.h
+++ b/tcpclt.h
@@ -26,7 +26,6 @@
#ifndef TCPCLT_H_INCLUDED
#define TCPCLT_H_INCLUDED 1
-//#include "tcpsyslog.h"
#include "obj.h"
/* the tcpclt object */
diff --git a/tcps_sess.c b/tcps_sess.c
index b5c9c31f..33c13aa0 100644
--- a/tcps_sess.c
+++ b/tcps_sess.c
@@ -27,22 +27,12 @@
*
* 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 "module-template.h"
@@ -51,11 +41,13 @@
#include "tcps_sess.h"
#include "obj.h"
#include "errmsg.h"
+#include "netstrm.h"
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(netstrm)
/* forward definitions */
static rsRetVal Close(tcps_sess_t *pThis);
@@ -64,7 +56,6 @@ static rsRetVal Close(tcps_sess_t *pThis);
/* Standard-Constructor
*/
BEGINobjConstruct(tcps_sess) /* be sure to specify the object type also in END macro! */
- pThis->sock = -1; /* no sock */
pThis->iMsg = 0; /* just make sure... */
pThis->bAtStrtOfFram = 1; /* indicate frame header expected */
pThis->eFraming = TCP_FRAMING_OCTET_STUFFING; /* just make sure... */
@@ -90,8 +81,8 @@ finalize_it:
/* destructor for the tcps_sess object */
BEGINobjDestruct(tcps_sess) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(tcps_sess)
- if(pThis->sock != -1)
- Close(pThis);
+ if(pThis->pStrm != NULL)
+ netstrm.Destruct(&pThis->pStrm);
if(pThis->pSrv->pOnSessDestruct != NULL) {
pThis->pSrv->pOnSessDestruct(&pThis->pUsr);
@@ -99,7 +90,6 @@ CODESTARTobjDestruct(tcps_sess)
/* now destruct our own properties */
if(pThis->fromHost != NULL)
free(pThis->fromHost);
- close(pThis->sock);
ENDobjDestruct(tcps_sess)
@@ -110,6 +100,10 @@ ENDobjDebugPrint(tcps_sess)
/* set property functions */
+/* set the hostname. Note that the caller *hands over* the string. That is,
+ * the caller no longer controls it once SetHost() has received it. Most importantly,
+ * the caller must not free it. -- gerhards, 2008-04-24
+ */
static rsRetVal
SetHost(tcps_sess_t *pThis, uchar *pszHost)
{
@@ -119,25 +113,24 @@ SetHost(tcps_sess_t *pThis, uchar *pszHost)
if(pThis->fromHost != NULL) {
free(pThis->fromHost);
- pThis->fromHost = NULL;
}
- if((pThis->fromHost = strdup((char*)pszHost)) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ pThis->fromHost = pszHost;
finalize_it:
RETiRet;
}
static rsRetVal
-SetSock(tcps_sess_t *pThis, int sock)
+SetStrm(tcps_sess_t *pThis, netstrm_t *pStrm)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
- pThis->sock = sock;
+ pThis->pStrm = pStrm;
RETiRet;
}
+
static rsRetVal
SetMsgIdx(tcps_sess_t *pThis, int idx)
{
@@ -200,9 +193,8 @@ PrepareClose(tcps_sess_t *pThis)
/* In this case, we have an invalid frame count and thus
* generate an error message and discard the frame.
*/
- errmsg.LogError(NO_ERRCODE, "Incomplete frame at end of stream in session %d - "
- "ignoring extra data (a message may be lost).\n",
- pThis->sock);
+ errmsg.LogError(NO_ERRCODE, "Incomplete frame at end of stream in session %p - "
+ "ignoring extra data (a message may be lost).\n", pThis->pStrm);
/* nothing more to do */
} else { /* here, we have traditional framing. Missing LF at the end
* of message may occur. As such, we process the message in
@@ -228,8 +220,7 @@ Close(tcps_sess_t *pThis)
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
- close(pThis->sock);
- pThis->sock = -1;
+ netstrm.Destruct(&pThis->pStrm);
free(pThis->fromHost);
pThis->fromHost = NULL; /* not really needed, but... */
@@ -403,7 +394,7 @@ CODESTARTobjQueryInterface(tcps_sess)
pIf->SetUsrP = SetUsrP;
pIf->SetTcpsrv = SetTcpsrv;
pIf->SetHost = SetHost;
- pIf->SetSock = SetSock;
+ pIf->SetStrm = SetStrm;
pIf->SetMsgIdx = SetMsgIdx;
finalize_it:
ENDobjQueryInterface(tcps_sess)
@@ -416,6 +407,7 @@ BEGINObjClassExit(tcps_sess, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END
CODESTARTObjClassExit(tcps_sess)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrm, LM_NETSTRM_FILENAME);
ENDObjClassExit(tcps_sess)
@@ -426,6 +418,7 @@ ENDObjClassExit(tcps_sess)
BEGINObjClassInit(tcps_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
/* set our own handlers */
OBJSetMethodHandler(objMethod_DEBUGPRINT, tcps_sessDebugPrint);
diff --git a/tcps_sess.h b/tcps_sess.h
index 1d45c482..9f3d10d6 100644
--- a/tcps_sess.h
+++ b/tcps_sess.h
@@ -32,8 +32,8 @@ struct tcpsrv_s;
typedef struct tcps_sess_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
struct tcpsrv_s *pSrv; /* pointer back to my server (e.g. for callbacks) */
- int sock;
- int iMsg; /* index of next char to store in msg */
+ netstrm_t *pStrm;
+ int iMsg; /* index of next char to store in msg */
int bAtStrtOfFram; /* are we at the very beginning of a new frame? */
enum {
eAtStrtFram,
@@ -43,7 +43,7 @@ typedef struct tcps_sess_s {
int iOctetsRemain; /* Number of Octets remaining in message */
TCPFRAMINGMODE eFraming;
char msg[MAXLINE+1];
- char *fromHost;
+ uchar *fromHost;
void *pUsr; /* a user-pointer */
} tcps_sess_t;
@@ -61,7 +61,7 @@ BEGINinterface(tcps_sess) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetTcpsrv)(tcps_sess_t *pThis, struct tcpsrv_s *pSrv);
rsRetVal (*SetUsrP)(tcps_sess_t*, void*);
rsRetVal (*SetHost)(tcps_sess_t *pThis, uchar*);
- rsRetVal (*SetSock)(tcps_sess_t *pThis, int);
+ rsRetVal (*SetStrm)(tcps_sess_t *pThis, netstrm_t*);
rsRetVal (*SetMsgIdx)(tcps_sess_t *pThis, int);
ENDinterface(tcps_sess)
#define tcps_sessCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
diff --git a/tcpsrv.c b/tcpsrv.c
index 499b0ce8..069c83c0 100644
--- a/tcpsrv.c
+++ b/tcpsrv.c
@@ -63,12 +63,16 @@
#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
@@ -77,30 +81,9 @@ DEFobjCurrIf(glbl)
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;
-}
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(nssel)
/* configure TCP listener settings. This is called during command
@@ -199,9 +182,10 @@ TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
register int i;
ISOBJ_TYPE_assert(pThis, tcpsrv);
- for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i)
+ for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) {
if(pThis->pSessions[i] != NULL)
break;
+ }
return((i < pThis->iSessMax) ? i : -1);
}
@@ -215,17 +199,17 @@ TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
*/
static void deinit_tcp_listener(tcpsrv_t *pThis)
{
- int iTCPSess;
+ int i;
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]);
+ i = TCPSessGetNxtSess(pThis, -1);
+ while(i != -1) {
+ tcps_sess.Destruct(&pThis->pSessions[i]);
/* now get next... */
- iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
+ i = TCPSessGetNxtSess(pThis, i);
}
/* we are done with the session table - so get rid of it...
@@ -236,33 +220,49 @@ static void deinit_tcp_listener(tcpsrv_t *pThis)
if(pThis->TCPLstnPort != NULL)
free(pThis->TCPLstnPort);
- /* finally close the listen sockets themselfs */
- freeAllSockets(&pThis->pSocksLstn);
+ /* finally close our listen streams */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ netstrm.Destruct(pThis->ppLstn + i);
+ }
}
-/* 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.
+/* add a listen socket to our listen socket array. This is a callback
+ * invoked from the netstrm class. -- rgerhards, 2008-04-23
*/
-static int *create_tcp_socket(tcpsrv_t *pThis)
+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)
{
- struct addrinfo hints, *res, *r;
- int error, maxs, *s, *socks, on = 1;
- char *TCPLstnPort;
+ DEFiRet;
+ uchar *TCPLstnPort;
ISOBJ_TYPE_assert(pThis, tcpsrv);
- if(!strcmp(pThis->TCPLstnPort, "0"))
- TCPLstnPort = "514";
+ 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
@@ -270,121 +270,10 @@ static int *create_tcp_socket(tcpsrv_t *pThis)
* 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 = glbl.GetDefPFFamily();
- 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
+ TCPLstnPort = (uchar*)pThis->TCPLstnPort;
- 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);
- }
+ /* 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
@@ -395,11 +284,11 @@ static int *create_tcp_socket(tcpsrv_t *pThis)
* 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);
+ ABORT_FINALIZE(RS_RET_ERR);
}
- return(socks);
+finalize_it:
+ RETiRet;
}
@@ -414,34 +303,25 @@ static int *create_tcp_socket(tcpsrv_t *pThis)
* rgerhards, 2008-03-02
*/
static rsRetVal
-SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, int fd)
+SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
{
DEFiRet;
tcps_sess_t *pSess;
- int newConn;
+ netstrm_t *pNewStrm = NULL;
int iSess = -1;
struct sockaddr_storage addr;
- socklen_t addrlen = sizeof(struct sockaddr_storage);
- uchar fromHost[NI_MAXHOST];
- uchar fromHostFQDN[NI_MAXHOST];
+ uchar *fromHostFQDN = NULL;
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;
- }
+ CHKiRet(netstrm.AcceptConnReq(pStrm, &pNewStrm));
/* 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;
+ ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED);
} else {
/* we found a free spot and can construct our session object */
CHKiRet(tcps_sess.Construct(&pSess));
@@ -450,38 +330,28 @@ SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, int fd)
/* 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;
- }
+ CHKiRet(netstrm.GetRemoteHName(pStrm, &fromHostFQDN));
+ /* 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
- * syslog messages. If it isn't, we do not further
- * process the message but log a warning (if we are
- * configured to do this).
+ /* 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", (char *) fromHostFQDN);
+ dbgprintf("%s is not an allowed sender\n", fromHostFQDN);
if(glbl.GetOption_DisallowWarning()) {
errno = 0;
- errmsg.LogError(NO_ERRCODE, "TCP message from disallowed sender %s discarded",
- (char*)fromHost);
+ errmsg.LogError(NO_ERRCODE, "TCP message from disallowed sender %s discarded", fromHostFQDN);
}
- 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.SetHost(pSess, fromHostFQDN));
+ 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));
@@ -500,79 +370,70 @@ finalize_it:
tcps_sess.Destruct(&pThis->pSessions[iSess]);
}
iSess = -1; // TODO: change this to be fully iRet compliant ;)
+ if(pNewStrm != NULL)
+ netstrm.Destruct(&pNewStrm);
}
RETiRet;
}
-/* This function is called to gather input.
- */
+static void
+RunCancelCleanup(void *arg)
+{
+ nssel_t **ppSel = (nssel_t**) arg;
+
+ if(*ppSel != NULL)
+ nssel.Destruct(ppSel);
+}
+/* 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;
+ int bIsReady;
tcps_sess_t *pNewSess;
+ nssel_t *pSel;
+ int state;
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.
+ /* 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) {
- 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);
- }
+ 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) {
+RUNLOG_VAR("%d", i);
+ CHKiRet(nssel.Add(pSel, pThis->ppLstn[i], NSDSEL_RD));
}
- if(Debug) {
- // TODO: name in dbgprintf!
- dbgprintf("--------<TCPSRV> calling select, active file descriptors (max %d): ", maxfds);
- for (nfds = 0; nfds <= maxfds; ++nfds)
- if ( FD_ISSET(nfds, &readfds) )
- dbgprintf("%d ", nfds);
- dbgprintf("\n");
+ /* 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 */
- nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
+ CHKiRet(nssel.Wait(pSel, &nfds));
- 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]);
- SessAccept(pThis, &pNewSess, pThis->pSocksLstn[i+1]);
+ 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 */
}
}
@@ -580,12 +441,10 @@ Run(tcpsrv_t *pThis)
/* 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)) {
+ CHKiRet(nssel.IsReady(pSel, pThis->pSessions[iTCPSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
char buf[MAXLINE];
- dbgprintf("tcp session socket with new data: #%d\n", fdSess);
+ dbgprintf("netstream %p with new data\n", pThis->pSessions[iTCPSess]->pStrm);
/* Receive message */
state = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf));
@@ -593,7 +452,8 @@ Run(tcpsrv_t *pThis)
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);
+ errmsg.LogError(NO_ERRCODE, "netstream session %p will be closed, error ignored\n",
+ pThis->pSessions[iTCPSess]->pStrm);
pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
} else {
@@ -612,29 +472,44 @@ Run(tcpsrv_t *pThis)
}
iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess);
}
+ CHKiRet(nssel.Destruct(&pSel));
}
+ /* note that this point is usually not reached */
+ pthread_cleanup_pop(0); /* remove cleanup handler */
+
+finalize_it: // TODO: think: is it really good to exit the loop?
RETiRet;
}
-/* Standard-Constructor
- */
+/* Standard-Constructor */
BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
- pThis->pSocksLstn = NULL;
- pThis->iSessMax = 200; /* TODO: useful default ;) */
+ pThis->iSessMax = TCPSESS_MAX_DEFAULT; /* TODO: useful default ;) */
ENDobjConstruct(tcpsrv)
-/* ConstructionFinalizer
- */
+/* ConstructionFinalizer */
static rsRetVal
-tcpsrvConstructFinalize(tcpsrv_t __attribute__((unused)) *pThis)
+tcpsrvConstructFinalize(tcpsrv_t *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcpsrv);
- pThis->pSocksLstn = pThis->OpenLstnSocks(pThis);
+ /* prepare network stream subsystem */
+ CHKiRet(netstrms.Construct(&pThis->pNS));
+ // 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;
}
@@ -646,6 +521,11 @@ CODESTARTobjDestruct(tcpsrv)
pThis->OnDestruct(pThis->pUsr);
deinit_tcp_listener(pThis);
+
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ if(pThis->ppLstn != NULL)
+ free(pThis->ppLstn);
ENDobjDestruct(tcpsrv)
@@ -728,7 +608,7 @@ SetCBOnErrClose(tcpsrv_t *pThis, rsRetVal (*pCB)(tcps_sess_t*))
}
static rsRetVal
-SetCBOpenLstnSocks(tcpsrv_t *pThis, int* (*pCB)(tcpsrv_t*))
+SetCBOpenLstnSocks(tcpsrv_t *pThis, rsRetVal (*pCB)(tcpsrv_t*))
{
DEFiRet;
pThis->OpenLstnSocks = pCB;
@@ -794,6 +674,9 @@ CODESTARTObjClassExit(tcpsrv)
objRelease(conf, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(nssel, LM_NSSEL_FILENAME);
+ objRelease(netstrm, LM_NETSTRM_FILENAME);
+ objRelease(netstrms, LM_NETSTRMS_FILENAME);
objRelease(net, LM_NET_FILENAME);
ENDObjClassExit(tcpsrv)
@@ -806,6 +689,9 @@ BEGINObjClassInit(tcpsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
+ CHKiRet(objUse(nssel, LM_NSSEL_FILENAME));
CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB));
CHKiRet(objUse(conf, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
diff --git a/tcpsrv.h b/tcpsrv.h
index 20a7ea25..ada77aec 100644
--- a/tcpsrv.h
+++ b/tcpsrv.h
@@ -26,9 +26,11 @@
#include "tcps_sess.h"
/* the tcpsrv object */
-typedef struct tcpsrv_s {
+struct tcpsrv_s {
BEGINobjInstance; /**< Data to implement generic object - MUST be the first data element! */
- int *pSocksLstn; /**< listen socket array for server [0] holds count */
+ netstrms_t *pNS; /**< pointer to network stream subsystem */
+ int iLstnMax; /**< max nbr of listeners currently supported */
+ netstrm_t **ppLstn; /**< our netstream listners */
int iSessMax; /**< max number of sessions supported */
char *TCPLstnPort; /**< the port the listener shall listen on */
tcps_sess_t **pSessions;/**< array of all of our sessions */
@@ -36,16 +38,16 @@ typedef struct tcpsrv_s {
/* callbacks */
int (*pIsPermittedHost)(struct sockaddr *addr, char *fromHostFQDN, void*pUsrSrv, void*pUsrSess);
int (*pRcvData)(tcps_sess_t*, char*, size_t);
- int* (*OpenLstnSocks)(struct tcpsrv_s*);
+ rsRetVal (*OpenLstnSocks)(struct tcpsrv_s*);
rsRetVal (*pOnListenDeinit)(void*);
rsRetVal (*OnDestruct)(void*);
rsRetVal (*pOnRegularClose)(tcps_sess_t *pSess);
rsRetVal (*pOnErrClose)(tcps_sess_t *pSess);
/* session specific callbacks */
- rsRetVal (*pOnSessAccept)(struct tcpsrv_s *, tcps_sess_t*);
+ rsRetVal (*pOnSessAccept)(tcpsrv_t *, tcps_sess_t*);
rsRetVal (*OnSessConstructFinalize)(void*);
rsRetVal (*pOnSessDestruct)(void*);
-} tcpsrv_t;
+};
/* interfaces */
@@ -55,13 +57,13 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*ConstructFinalize)(tcpsrv_t __attribute__((unused)) *pThis);
rsRetVal (*Destruct)(tcpsrv_t **ppThis);
void (*configureTCPListen)(tcpsrv_t*, char *cOptarg);
- int (*SessAccept)(tcpsrv_t *pThis, tcps_sess_t**ppSess, int fd);
- int* (*create_tcp_socket)(tcpsrv_t *pThis);
+ rsRetVal (*SessAccept)(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm);
+ rsRetVal (*create_tcp_socket)(tcpsrv_t *pThis);
rsRetVal (*Run)(tcpsrv_t *pThis);
/* set methods */
rsRetVal (*SetUsrP)(tcpsrv_t*, void*);
rsRetVal (*SetCBIsPermittedHost)(tcpsrv_t*, int (*) (struct sockaddr *addr, char*, void*, void*));
- rsRetVal (*SetCBOpenLstnSocks)(tcpsrv_t *, int* (*)(tcpsrv_t*));
+ rsRetVal (*SetCBOpenLstnSocks)(tcpsrv_t *, rsRetVal (*)(tcpsrv_t*));
rsRetVal (*SetCBRcvData)(tcpsrv_t *, int (*)(tcps_sess_t*, char*, size_t));
rsRetVal (*SetCBOnListenDeinit)(tcpsrv_t*, rsRetVal (*)(void*));
rsRetVal (*SetCBOnDestruct)(tcpsrv_t*, rsRetVal (*) (void*));
@@ -72,7 +74,7 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetCBOnSessDestruct)(tcpsrv_t*, rsRetVal (*) (void*));
rsRetVal (*SetCBOnSessConstructFinalize)(tcpsrv_t*, rsRetVal (*) (void*));
ENDinterface(tcpsrv)
-#define tcpsrvCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define tcpsrvCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* prototypes */
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 00000000..f00abf0b
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,42 @@
+sbin_PROGRAMS =
+man_MANS =
+
+sbin_PROGRAMS += rsyslogd
+rsyslogd_SOURCES = \
+ syslogd.c \
+ syslogd.h \
+ omshell.c \
+ omshell.h \
+ omusrmsg.c \
+ omusrmsg.h \
+ omfwd.c \
+ omfwd.h \
+ omfile.c \
+ omfile.h \
+ omdiscard.c \
+ omdiscard.h \
+ iminternal.c \
+ iminternal.h \
+ pidfile.c \
+ pidfile.h \
+ \
+ ../action.h \
+ ../action.c \
+ ../threads.c \
+ ../threads.h \
+ \
+ ../parse.c \
+ ../parse.h \
+ \
+ ../outchannel.c \
+ ../outchannel.h \
+ ../template.c \
+ ../template.h \
+ ../conf.c \
+ ../conf.h
+
+rsyslogd_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+rsyslogd_LDADD = $(zlib_libs) $(pthreads_libs) $(dl_libs) $(rt_libs) $(rsrt_libs)
+rsyslogd_LDFLAGS = -export-dynamic
+
+man_MANS += rsyslogd.8 rsyslog.conf.5
diff --git a/tools/omfwd.c b/tools/omfwd.c
index 80f62c8a..719075c7 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -30,7 +30,6 @@
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
-#ifdef SYSLOG_INET
#include "rsyslog.h"
#include <stdio.h>
#include <stdarg.h>
@@ -52,6 +51,8 @@
#include "syslogd-types.h"
#include "srUtils.h"
#include "net.h"
+#include "netstrms.h"
+#include "netstrm.h"
#include "omfwd.h"
#include "template.h"
#include "msg.h"
@@ -69,17 +70,16 @@ DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(net)
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
DEFobjCurrIf(tcpclt)
typedef struct _instanceData {
+ netstrms_t *pNS; /* netstream subsystem */
+ netstrm_t *pNetstrm; /* our output netstream */
char *f_hname;
- short sock; /* file descriptor */
int *pSockArray; /* sockets to use for UDP */
- enum { /* TODO: we shoud revisit these definitions */
- eDestFORW,
- eDestFORW_SUSP,
- eDestFORW_UNKN
- } eDestState;
+ int bIsConnected; /* are we connected to remote host? 0 - no, 1 - yes, UDP means addr resolved */
struct addrinfo *f_addr;
int compressionLevel; /* 0 - no compression, else level for zlib */
char *port;
@@ -87,8 +87,7 @@ typedef struct _instanceData {
# define FORW_UDP 0
# define FORW_TCP 1
/* following fields for TCP-based delivery */
- time_t ttSuspend; /* time selector was suspended */
- tcpclt_t *pTCPClt; /* our tcpclt object */
+ tcpclt_t *pTCPClt; /* our tcpclt object */
} instanceData;
/* config data */
@@ -101,7 +100,7 @@ static uchar *pszTplName = NULL; /* name of the default template to use */
* We may change the implementation to try to lookup the port
* if it is unspecified. So far, we use the IANA default auf 514.
*/
-static char *getFwdSyslogPt(instanceData *pData)
+static char *getFwdPt(instanceData *pData)
{
assert(pData != NULL);
if(pData->port == NULL)
@@ -112,7 +111,6 @@ static char *getFwdSyslogPt(instanceData *pData)
BEGINcreateInstance
CODESTARTcreateInstance
- pData->sock = -1;
ENDcreateInstance
@@ -125,20 +123,16 @@ ENDisCompatibleWithFeature
BEGINfreeInstance
CODESTARTfreeInstance
- switch (pData->eDestState) {
- case eDestFORW:
- case eDestFORW_SUSP:
- freeaddrinfo(pData->f_addr);
- /* fall through */
- case eDestFORW_UNKN:
- if(pData->port != NULL)
- free(pData->port);
- break;
+ if(pData->f_addr != NULL) { /* TODO: is the check ok? */
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
}
+ if(pData->port != NULL)
+ free(pData->port);
/* final cleanup */
- if(pData->sock >= 0)
- close(pData->sock);
+ if(pData->pNetstrm != NULL)
+ netstrm.Destruct(&pData->pNetstrm);
if(pData->pSockArray != NULL)
net.closeUDPListenSockets(pData->pSockArray);
@@ -175,8 +169,7 @@ static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len)
* succeeding. We track this be bSendSuccess. We can not simply
* rely on lsent, as a call might initially work, but a later
* call fails. Then, lsent has the error status, even though
- * the sendto() succeeded.
- * rgerhards, 2007-06-22
+ * the sendto() succeeded. -- rgerhards, 2007-06-22
*/
bSendSuccess = FALSE;
for (r = pData->f_addr; r; r = r->ai_next) {
@@ -205,6 +198,7 @@ static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len)
RETiRet;
}
+
/* CODE FOR SENDING TCP MESSAGES */
@@ -217,22 +211,11 @@ static rsRetVal TCPSendFrame(void *pvData, char *msg, size_t len)
ssize_t lenSend;
instanceData *pData = (instanceData *) pvData;
- lenSend = send(pData->sock, msg, len, 0);
+ lenSend = len;
+ CHKiRet(netstrm.Send(pData->pNetstrm, (uchar*)msg, &lenSend));
dbgprintf("TCP sent %ld bytes, requested %ld\n", (long) lenSend, (long) len);
- if(lenSend == -1) {
- /* we have an error case - check what we can live with */
- switch(errno) {
- case EMSGSIZE:
- dbgprintf("message not (tcp)send, too large\n");
- /* This is not a real error, so it is not flagged as one */
- break;
- default:
- dbgprintf("message not (tcp)send");
- iRet = RS_RET_TCP_SEND_ERROR;
- break;
- }
- } else if(lenSend != (ssize_t) len) {
+ if(lenSend != (ssize_t) len) {
/* no real error, could "just" not send everything...
* For the time being, we ignore this...
* rgerhards, 2005-10-25
@@ -242,6 +225,7 @@ static rsRetVal TCPSendFrame(void *pvData, char *msg, size_t len)
/* TODO: we need to revisit this code -- rgerhards, 2007-12-28 */
}
+finalize_it:
RETiRet;
}
@@ -256,13 +240,12 @@ static rsRetVal TCPSendPrepRetry(void *pvData)
instanceData *pData = (instanceData *) pvData;
assert(pData != NULL);
- close(pData->sock);
- pData->sock = -1;
+ netstrm.Destruct(&pData->pNetstrm);
RETiRet;
}
-/* initialies everything so that TCPSend can work.
+/* initializes everything so that TCPSend can work.
* rgerhards, 2007-12-28
*/
static rsRetVal TCPSendInit(void *pvData)
@@ -271,9 +254,24 @@ static rsRetVal TCPSendInit(void *pvData)
instanceData *pData = (instanceData *) pvData;
assert(pData != NULL);
- if(pData->sock < 0) {
- if((pData->sock = tcpclt.CreateSocket(pData->f_addr)) < 0)
- iRet = RS_RET_TCP_SOCKCREATE_ERR;
+ if(pData->pNetstrm == NULL) {
+ CHKiRet(netstrms.Construct(&pData->pNS));
+ /* here we may set another netstream driver (e.g. to do TLS) */
+ CHKiRet(netstrms.ConstructFinalize(pData->pNS));
+
+ /* now create the actual stream and connect to the server */
+ CHKiRet(netstrms.CreateStrm(pData->pNS, &pData->pNetstrm));
+ CHKiRet(netstrm.ConstructFinalize(pData->pNetstrm));
+ CHKiRet(netstrm.Connect(pData->pNetstrm, glbl.GetDefPFFamily(),
+ (uchar*)pData->port, (uchar*)pData->f_hname));
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pData->pNetstrm != NULL)
+ netstrm.Destruct(&pData->pNetstrm);
+ if(pData->pNS != NULL)
+ netstrms.Destruct(&pData->pNS);
}
RETiRet;
@@ -288,39 +286,38 @@ static rsRetVal doTryResume(instanceData *pData)
DEFiRet;
struct addrinfo *res;
struct addrinfo hints;
- unsigned e;
-
- switch (pData->eDestState) {
- case eDestFORW_SUSP:
- iRet = RS_RET_OK; /* the actual check happens during doAction() only */
- pData->eDestState = eDestFORW;
- break;
-
- case eDestFORW_UNKN:
- /* The remote address is not yet known and needs to be obtained */
- dbgprintf(" %s\n", pData->f_hname);
+
+ if(pData->bIsConnected)
+ FINALIZE;
+
+ /* The remote address is not yet known and needs to be obtained */
+ dbgprintf(" %s\n", pData->f_hname);
+ if(pData->protocol == FORW_UDP) {
memset(&hints, 0, sizeof(hints));
- /* port must be numeric, because config file syntax requests this */
- /* TODO: this code is a duplicate from cfline() - we should later create
- * a common function.
- */
+ /* port must be numeric, because config file syntax requires this */
hints.ai_flags = AI_NUMERICSERV;
hints.ai_family = glbl.GetDefPFFamily();
- hints.ai_socktype = pData->protocol == FORW_UDP ? SOCK_DGRAM : SOCK_STREAM;
- if((e = getaddrinfo(pData->f_hname,
- getFwdSyslogPt(pData), &hints, &res)) == 0) {
- dbgprintf("%s found, resuming.\n", pData->f_hname);
- pData->f_addr = res;
- pData->eDestState = eDestFORW;
- } else {
- iRet = RS_RET_SUSPENDED;
+ hints.ai_socktype = pData->protocol == SOCK_DGRAM;
+ if((getaddrinfo(pData->f_hname, getFwdPt(pData), &hints, &res)) != 0) {
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ dbgprintf("%s found, resuming.\n", pData->f_hname);
+ pData->f_addr = res;
+ pData->bIsConnected = 1;
+ if(pData->pSockArray == NULL) {
+ pData->pSockArray = net.create_udp_socket((uchar*)pData->f_hname, NULL, 0);
+ }
+ } else {
+ CHKiRet(TCPSendInit((void*)pData));
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pData->f_addr != NULL) {
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
}
- break;
- case eDestFORW:
- /* rgerhards, 2007-09-11: this can not happen, but I've included it to
- * a) make the compiler happy, b) detect any logic errors */
- assert(0);
- break;
+ iRet = RS_RET_SUSPENDED;
}
RETiRet;
@@ -336,260 +333,238 @@ BEGINdoAction
char *psz; /* temporary buffering */
register unsigned l;
CODESTARTdoAction
- switch (pData->eDestState) {
- case eDestFORW_SUSP:
- dbgprintf("internal error in omfwd.c, eDestFORW_SUSP in doAction()!\n");
- iRet = RS_RET_SUSPENDED;
- break;
-
- case eDestFORW_UNKN:
- dbgprintf("doAction eDestFORW_UNKN\n");
- iRet = doTryResume(pData);
- break;
-
- case eDestFORW:
- dbgprintf(" %s:%s/%s\n", pData->f_hname, getFwdSyslogPt(pData),
- pData->protocol == FORW_UDP ? "udp" : "tcp");
- /* with UDP, check if the socket is there and, if not, alloc
- * it. TODO: there should be a better place for that code.
- * rgerhards, 2007-12-26
- */
- if(pData->protocol == FORW_UDP) {
- if(pData->pSockArray == NULL) {
- pData->pSockArray = net.create_udp_socket((uchar*)pData->f_hname, NULL, 0);
- }
- }
- pData->ttSuspend = time(NULL);
- psz = (char*) ppString[0];
- l = strlen((char*) psz);
- if (l > MAXLINE)
- l = MAXLINE;
-
-# ifdef USE_NETZIP
- /* Check if we should compress and, if so, do it. We also
- * check if the message is large enough to justify compression.
- * The smaller the message, the less likely is a gain in compression.
- * To save CPU cycles, we do not try to compress very small messages.
- * What "very small" means needs to be configured. Currently, it is
- * hard-coded but this may be changed to a config parameter.
- * rgerhards, 2006-11-30
- */
- if(pData->compressionLevel && (l > MIN_SIZE_FOR_COMPRESS)) {
- Bytef out[MAXLINE+MAXLINE/100+12] = "z";
- uLongf destLen = sizeof(out) / sizeof(Bytef);
- uLong srcLen = l;
- int ret;
- ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz,
- srcLen, pData->compressionLevel);
- dbgprintf("Compressing message, length was %d now %d, return state %d.\n",
- l, (int) destLen, ret);
- if(ret != Z_OK) {
- /* if we fail, we complain, but only in debug mode
- * Otherwise, we are silent. In any case, we ignore the
- * failed compression and just sent the uncompressed
- * data, which is still valid. So this is probably the
- * best course of action.
- * rgerhards, 2006-11-30
- */
- dbgprintf("Compression failed, sending uncompressed message\n");
- } else if(destLen+1 < l) {
- /* only use compression if there is a gain in using it! */
- dbgprintf("there is gain in compression, so we do it\n");
- psz = (char*) out;
- l = destLen + 1; /* take care for the "z" at message start! */
- }
- ++destLen;
+ CHKiRet(doTryResume(pData));
+
+ dbgprintf(" %s:%s/%s\n", pData->f_hname, getFwdPt(pData),
+ pData->protocol == FORW_UDP ? "udp" : "tcp");
+
+ psz = (char*) ppString[0];
+ l = strlen((char*) psz);
+ if (l > MAXLINE)
+ l = MAXLINE;
+
+# ifdef USE_NETZIP
+ /* Check if we should compress and, if so, do it. We also
+ * check if the message is large enough to justify compression.
+ * The smaller the message, the less likely is a gain in compression.
+ * To save CPU cycles, we do not try to compress very small messages.
+ * What "very small" means needs to be configured. Currently, it is
+ * hard-coded but this may be changed to a config parameter.
+ * rgerhards, 2006-11-30
+ */
+ if(pData->compressionLevel && (l > MIN_SIZE_FOR_COMPRESS)) {
+ Bytef out[MAXLINE+MAXLINE/100+12] = "z";
+ uLongf destLen = sizeof(out) / sizeof(Bytef);
+ uLong srcLen = l;
+ int ret;
+ ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz,
+ srcLen, pData->compressionLevel);
+ dbgprintf("Compressing message, length was %d now %d, return state %d.\n",
+ l, (int) destLen, ret);
+ if(ret != Z_OK) {
+ /* if we fail, we complain, but only in debug mode
+ * Otherwise, we are silent. In any case, we ignore the
+ * failed compression and just sent the uncompressed
+ * data, which is still valid. So this is probably the
+ * best course of action.
+ * rgerhards, 2006-11-30
+ */
+ dbgprintf("Compression failed, sending uncompressed message\n");
+ } else if(destLen+1 < l) {
+ /* only use compression if there is a gain in using it! */
+ dbgprintf("there is gain in compression, so we do it\n");
+ psz = (char*) out;
+ l = destLen + 1; /* take care for the "z" at message start! */
}
-# endif
+ ++destLen;
+ }
+# endif
- if(pData->protocol == FORW_UDP) {
- /* forward via UDP */
- CHKiRet(UDPSend(pData, psz, l));
- } else {
- /* forward via TCP */
- rsRetVal ret;
- ret = tcpclt.Send(pData->pTCPClt, pData, psz, l);
- if(ret != RS_RET_OK) {
- /* error! */
- dbgprintf("error forwarding via tcp, suspending\n");
- pData->eDestState = eDestFORW_SUSP;
- iRet = RS_RET_SUSPENDED;
- }
+ if(pData->protocol == FORW_UDP) {
+ /* forward via UDP */
+ CHKiRet(UDPSend(pData, psz, l));
+ } else {
+ /* forward via TCP */
+ rsRetVal ret;
+ ret = tcpclt.Send(pData->pTCPClt, pData, psz, l);
+ if(ret != RS_RET_OK) {
+ /* error! */
+ dbgprintf("error forwarding via tcp, suspending\n");
+ iRet = RS_RET_SUSPENDED;
}
- break;
}
finalize_it:
ENDdoAction
+/* This function loads TCP support, if not already loaded. It will be called
+ * during config processing. To server ressources, TCP support will only
+ * be loaded if it actually is used. -- rgerhard, 2008-04-17
+ */
+static rsRetVal
+loadTCPSupport(void)
+{
+ DEFiRet;
+ if(!netstrms.ifIsLoaded)
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+ if(!netstrm.ifIsLoaded)
+ CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
+ if(!tcpclt.ifIsLoaded)
+ CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME));
+
+finalize_it:
+ RETiRet;
+}
+
+
BEGINparseSelectorAct
uchar *q;
int i;
- int error;
int bErr;
- struct addrinfo hints, *res;
+ rsRetVal localRet;
+ struct addrinfo;
TCPFRAMINGMODE tcp_framing = TCP_FRAMING_OCTET_STUFFING;
CODESTARTparseSelectorAct
CODE_STD_STRING_REQUESTparseSelectorAct(1)
- if(*p == '@') {
- if((iRet = createInstance(&pData)) != RS_RET_OK)
- goto finalize_it;
- ++p; /* eat '@' */
- if(*p == '@') { /* indicator for TCP! */
- pData->protocol = FORW_TCP;
- ++p; /* eat this '@', too */
- } else {
- pData->protocol = FORW_UDP;
+ if(*p != '@')
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+
+ CHKiRet(createInstance(&pData));
+
+ ++p; /* eat '@' */
+ if(*p == '@') { /* indicator for TCP! */
+ localRet = loadTCPSupport();
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(NO_ERRCODE, "could not activate network stream modules for TCP "
+ "(internal error %d) - are modules missing?", localRet);
+ ABORT_FINALIZE(localRet);
}
- /* we are now after the protocol indicator. Now check if we should
- * use compression. We begin to use a new option format for this:
- * @(option,option)host:port
- * The first option defined is "z[0..9]" where the digit indicates
- * the compression level. If it is not given, 9 (best compression) is
- * assumed. An example action statement might be:
- * @@(z5,o)127.0.0.1:1400
- * Which means send via TCP with medium (5) compresion (z) to the local
- * host on port 1400. The '0' option means that octet-couting (as in
- * IETF I-D syslog-transport-tls) is to be used for framing (this option
- * applies to TCP-based syslog only and is ignored when specified with UDP).
- * That is not yet implemented.
- * rgerhards, 2006-12-07
- */
- if(*p == '(') {
- /* at this position, it *must* be an option indicator */
- do {
- ++p; /* eat '(' or ',' (depending on when called) */
- /* check options */
- if(*p == 'z') { /* compression */
-# ifdef USE_NETZIP
- ++p; /* eat */
- if(isdigit((int) *p)) {
- int iLevel;
- iLevel = *p - '0';
- ++p; /* eat */
- pData->compressionLevel = iLevel;
- } else {
- errmsg.LogError(NO_ERRCODE, "Invalid compression level '%c' specified in "
- "forwardig action - NOT turning on compression.",
- *p);
- }
-# else
- errmsg.LogError(NO_ERRCODE, "Compression requested, but rsyslogd is not compiled "
- "with compression support - request ignored.");
-# endif /* #ifdef USE_NETZIP */
- } else if(*p == 'o') { /* octet-couting based TCP framing? */
+ pData->protocol = FORW_TCP;
+ ++p; /* eat this '@', too */
+ } else {
+ pData->protocol = FORW_UDP;
+ }
+ /* we are now after the protocol indicator. Now check if we should
+ * use compression. We begin to use a new option format for this:
+ * @(option,option)host:port
+ * The first option defined is "z[0..9]" where the digit indicates
+ * the compression level. If it is not given, 9 (best compression) is
+ * assumed. An example action statement might be:
+ * @@(z5,o)127.0.0.1:1400
+ * Which means send via TCP with medium (5) compresion (z) to the local
+ * host on port 1400. The '0' option means that octet-couting (as in
+ * IETF I-D syslog-transport-tls) is to be used for framing (this option
+ * applies to TCP-based syslog only and is ignored when specified with UDP).
+ * That is not yet implemented.
+ * rgerhards, 2006-12-07
+ */
+ if(*p == '(') {
+ /* at this position, it *must* be an option indicator */
+ do {
+ ++p; /* eat '(' or ',' (depending on when called) */
+ /* check options */
+ if(*p == 'z') { /* compression */
+# ifdef USE_NETZIP
+ ++p; /* eat */
+ if(isdigit((int) *p)) {
+ int iLevel;
+ iLevel = *p - '0';
++p; /* eat */
- /* no further options settable */
- tcp_framing = TCP_FRAMING_OCTET_COUNTING;
- } else { /* invalid option! Just skip it... */
- errmsg.LogError(NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p);
- ++p; /* eat invalid option */
- }
- /* the option processing is done. We now do a generic skip
- * to either the next option or the end of the option
- * block.
- */
- while(*p && *p != ')' && *p != ',')
- ++p; /* just skip it */
- } while(*p && *p == ','); /* Attention: do.. while() */
- if(*p == ')')
- ++p; /* eat terminator, on to next */
- else
- /* we probably have end of string - leave it for the rest
- * of the code to handle it (but warn the user)
- */
- errmsg.LogError(NO_ERRCODE, "Option block not terminated in forwarding action.");
- }
- /* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
- * now skip to port and then template name. rgerhards 2005-07-06
- */
- for(q = p ; *p && *p != ';' && *p != ':' ; ++p)
- /* JUST SKIP */;
-
- pData->port = NULL;
- if(*p == ':') { /* process port */
- uchar * tmp;
-
- *p = '\0'; /* trick to obtain hostname (later)! */
- tmp = ++p;
- for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i)
- /* SKIP AND COUNT */;
- pData->port = malloc(i + 1);
- if(pData->port == NULL) {
- errmsg.LogError(NO_ERRCODE, "Could not get memory to store syslog forwarding port, "
- "using default port, results may not be what you intend\n");
- /* we leave f_forw.port set to NULL, this is then handled by
- * getFwdSyslogPt().
- */
- } else {
- memcpy(pData->port, tmp, i);
- *(pData->port + i) = '\0';
- }
- }
-
- /* now skip to template */
- bErr = 0;
- while(*p && *p != ';') {
- if(*p && *p != ';' && !isspace((int) *p)) {
- if(bErr == 0) { /* only 1 error msg! */
- bErr = 1;
- errno = 0;
- errmsg.LogError(NO_ERRCODE, "invalid selector line (port), probably not doing "
- "what was intended");
+ pData->compressionLevel = iLevel;
+ } else {
+ errmsg.LogError(NO_ERRCODE, "Invalid compression level '%c' specified in "
+ "forwardig action - NOT turning on compression.",
+ *p);
}
+# else
+ errmsg.LogError(NO_ERRCODE, "Compression requested, but rsyslogd is not compiled "
+ "with compression support - request ignored.");
+# endif /* #ifdef USE_NETZIP */
+ } else if(*p == 'o') { /* octet-couting based TCP framing? */
+ ++p; /* eat */
+ /* no further options settable */
+ tcp_framing = TCP_FRAMING_OCTET_COUNTING;
+ } else { /* invalid option! Just skip it... */
+ errmsg.LogError(NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p);
+ ++p; /* eat invalid option */
}
- ++p;
+ /* the option processing is done. We now do a generic skip
+ * to either the next option or the end of the option
+ * block.
+ */
+ while(*p && *p != ')' && *p != ',')
+ ++p; /* just skip it */
+ } while(*p && *p == ','); /* Attention: do.. while() */
+ if(*p == ')')
+ ++p; /* eat terminator, on to next */
+ else
+ /* we probably have end of string - leave it for the rest
+ * of the code to handle it (but warn the user)
+ */
+ errmsg.LogError(NO_ERRCODE, "Option block not terminated in forwarding action.");
+ }
+ /* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
+ * now skip to port and then template name. rgerhards 2005-07-06
+ */
+ for(q = p ; *p && *p != ';' && *p != ':' ; ++p)
+ /* JUST SKIP */;
+
+ pData->port = NULL;
+ if(*p == ':') { /* process port */
+ uchar * tmp;
+
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ tmp = ++p;
+ for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i)
+ /* SKIP AND COUNT */;
+ pData->port = malloc(i + 1);
+ if(pData->port == NULL) {
+ errmsg.LogError(NO_ERRCODE, "Could not get memory to store syslog forwarding port, "
+ "using default port, results may not be what you intend\n");
+ /* we leave f_forw.port set to NULL, this is then handled by getFwdPt(). */
+ } else {
+ memcpy(pData->port, tmp, i);
+ *(pData->port + i) = '\0';
}
+ }
- /* TODO: make this if go away! */
- if(*p == ';') {
- *p = '\0'; /* trick to obtain hostname (later)! */
- CHKmalloc(pData->f_hname = strdup((char*) q));
- *p = ';';
- } else {
- CHKmalloc(pData->f_hname = strdup((char*) q));
+ /* now skip to template */
+ bErr = 0;
+ while(*p && *p != ';') {
+ if(*p && *p != ';' && !isspace((int) *p)) {
+ if(bErr == 0) { /* only 1 error msg! */
+ bErr = 1;
+ errno = 0;
+ errmsg.LogError(NO_ERRCODE, "invalid selector line (port), probably not doing "
+ "what was intended");
+ }
}
+ ++p;
+ }
- /* process template */
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName));
+ /* TODO: make this if go away! */
+ if(*p == ';') {
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ CHKmalloc(pData->f_hname = strdup((char*) q));
+ *p = ';';
+ } else {
+ CHKmalloc(pData->f_hname = strdup((char*) q));
+ }
- /* first set the pData->eDestState */
- memset(&hints, 0, sizeof(hints));
- /* port must be numeric, because config file syntax requests this */
- hints.ai_flags = AI_NUMERICSERV;
- hints.ai_family = glbl.GetDefPFFamily();
- hints.ai_socktype = pData->protocol == FORW_UDP ? SOCK_DGRAM : SOCK_STREAM;
- if( (error = getaddrinfo(pData->f_hname, getFwdSyslogPt(pData), &hints, &res)) != 0) {
- pData->eDestState = eDestFORW_UNKN;
- pData->ttSuspend = time(NULL);
- } else {
- pData->eDestState = eDestFORW;
- pData->f_addr = res;
- }
- /*
- * Otherwise the host might be unknown due to an
- * inaccessible nameserver (perhaps on the same
- * host). We try to get the ip number later, like
- * FORW_SUSP.
- */
- if(pData->protocol == FORW_TCP) {
- /* create our tcpclt */
- CHKiRet(tcpclt.Construct(&pData->pTCPClt));
- /* and set callbacks */
- CHKiRet(tcpclt.SetSendInit(pData->pTCPClt, TCPSendInit));
- CHKiRet(tcpclt.SetSendFrame(pData->pTCPClt, TCPSendFrame));
- CHKiRet(tcpclt.SetSendPrepRetry(pData->pTCPClt, TCPSendPrepRetry));
- CHKiRet(tcpclt.SetFraming(pData->pTCPClt, tcp_framing));
- }
+ /* process template */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName));
- } else {
- iRet = RS_RET_CONFLINE_UNPROCESSED;
+ if(pData->protocol == FORW_TCP) {
+ /* create our tcpclt */
+ CHKiRet(tcpclt.Construct(&pData->pTCPClt));
+ /* and set callbacks */
+ CHKiRet(tcpclt.SetSendInit(pData->pTCPClt, TCPSendInit));
+ CHKiRet(tcpclt.SetSendFrame(pData->pTCPClt, TCPSendFrame));
+ CHKiRet(tcpclt.SetSendPrepRetry(pData->pTCPClt, TCPSendPrepRetry));
+ CHKiRet(tcpclt.SetFraming(pData->pTCPClt, tcp_framing));
}
- /* TODO: do we need to call freeInstance if we failed - this is a general question for
- * all output modules. I'll address it lates as the interface evolves. rgerhards, 2007-07-25
- */
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -600,7 +575,12 @@ CODESTARTmodExit
objRelease(errmsg, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
- objRelease(tcpclt, LM_TCPCLT_FILENAME);
+ if(netstrm.ifIsLoaded)
+ objRelease(netstrm, LM_NETSTRM_FILENAME);
+ if(netstrms.ifIsLoaded)
+ objRelease(netstrms, LM_NETSTRMS_FILENAME);
+ if(!tcpclt.ifIsLoaded)
+ objRelease(tcpclt, LM_TCPCLT_FILENAME);
if(pszTplName != NULL) {
free(pszTplName);
@@ -635,13 +615,11 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(objUse(net, LM_NET_FILENAME));
- CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME));
+ CHKiRet(objUse(net,LM_NET_FILENAME));
CHKiRet(regCfSysLineHdlr((uchar *)"actionforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
-#endif /* #ifdef SYSLOG_INET */
/* vim:set ai:
*/
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 870925a2..835a020d 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -1553,8 +1553,7 @@ submitMsg(msg_t *pMsg)
}
-/*
- * Log a message to the appropriate log files, users, etc. based on
+/* Log a message to the appropriate log files, users, etc. based on
* the priority.
* rgerhards 2004-11-08: actually, this also decodes all but the PRI part.
* rgerhards 2004-11-09: ... but only, if syslogd could properly be initialized
@@ -2208,12 +2207,12 @@ init(void)
tplDeleteNew();
/* re-setting values to defaults (where applicable) */
- /* TODO: once we have loadable modules, we must re-visit this code. The reason is
+ /* once we have loadable modules, we must re-visit this code. The reason is
* that config variables are not re-set, because the module is not yet loaded. On
* the other hand, that doesn't matter, because the module got unloaded and is then
- * re-loaded, so the variables should be re-set via that way. In any case, we should
- * think about the whole situation when we implement loadable plugins.
- * rgerhards, 2007-07-31
+ * re-loaded, so the variables should be re-set via that way. And this is exactly how
+ * it works. Loadable module's variables are initialized on load, the rest here.
+ * rgerhards, 2008-04-28
*/
conf.cfsysline((uchar*)"ResetConfigVariables");