summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2010-08-10 14:51:43 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2010-08-10 14:51:43 +0200
commit55256ac96815d6e13fc9df7206d50ef7dcaca4fe (patch)
treeb2a6a8be01f33f53c12f937aefc88173b581a952
parent318adf58de35d7b8c6ca891b93c351220628586b (diff)
downloadrsyslog-55256ac96815d6e13fc9df7206d50ef7dcaca4fe.tar.gz
rsyslog-55256ac96815d6e13fc9df7206d50ef7dcaca4fe.tar.xz
rsyslog-55256ac96815d6e13fc9df7206d50ef7dcaca4fe.zip
added imptcp
imptcp is a simplified, Linux-specific and potentielly fast syslog plain tcp input plugin (NOT supporting TLS!)
-rw-r--r--ChangeLog2
-rw-r--r--Makefile.am7
-rw-r--r--configure.ac15
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/imptcp.html84
-rw-r--r--doc/rsyslog_conf_modules.html3
-rw-r--r--plugins/imptcp/Makefile.am6
-rw-r--r--plugins/imptcp/imptcp.c1146
-rw-r--r--runtime/rsyslog.h4
-rw-r--r--tools/syslogd.c2
10 files changed, 1267 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index df29694c..b8cbdb42 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+- added imptcp, a simplified, Linux-specific and potentielly fast
+ syslog plain tcp input plugin (NOT supporting TLS!)
---------------------------------------------------------------------------
Version 4.6.4 [v4-stable] (rgerhards), 2010-08.05
- bugfix: zero-sized (empty) messages were processed by imtcp
diff --git a/Makefile.am b/Makefile.am
index f5f9a6ed..439cb417 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -103,6 +103,10 @@ if ENABLE_IMFILE
SUBDIRS += plugins/imfile
endif
+if ENABLE_IMPTCP
+SUBDIRS += plugins/imptcp
+endif
+
if ENABLE_IMDIAG
SUBDIRS += plugins/imdiag
endif
@@ -134,5 +138,6 @@ SUBDIRS += tests
# temporarily be removed below. The intent behind forcing everthing to compile
# in a make distcheck is so that we detect code that accidently was not updated
# when some global update happened.
-DISTCHECK_CONFIGURE_FLAGS=--enable-gssapi_krb5 --enable-imfile --enable-snmp --enable-pgsql --enable-libdbi --enable-mysql --enable-omtemplate --enable-imtemplate --enable-relp --enable-rsyslogd --enable-mail --enable-klog --enable-diagtools --enable-gnutls --enable-omstdout --enable-omprog --enable-imdiag --enable-shave --enable-extended-tests
+DISTCHECK_CONFIGURE_FLAGS=--enable-gssapi_krb5 --enable-imfile --enable-snmp --enable-pgsql --enable-libdbi --enable-mysql --enable-omtemplate --enable-imtemplate --enable-relp --enable-rsyslogd --enable-mail --enable-klog --enable-diagtools --enable-gnutls --enable-omstdout --enable-omprog --enable-imdiag --enable-shave --enable-extended-tests \
+ --enable-imptcp
ACLOCAL_AMFLAGS = -I m4
diff --git a/configure.ac b/configure.ac
index bc3965d5..ce34e53d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -727,6 +727,19 @@ AC_ARG_ENABLE(imfile,
AM_CONDITIONAL(ENABLE_IMFILE, test x$enable_imfile = xyes)
+# settings for the ptcp input module
+AC_ARG_ENABLE(imptcp,
+ [AS_HELP_STRING([--enable-imptcp],[plain tcp input module enabled @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_imptcp="yes" ;;
+ no) enable_imptcp="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-imptcp) ;;
+ esac],
+ [enable_imptcp=no]
+)
+AM_CONDITIONAL(ENABLE_IMPTCP, test x$enable_imptcp = xyes)
+
+
# settings for the omprog output module
AC_ARG_ENABLE(omprog,
[AS_HELP_STRING([--enable-omprog],[Compiles omprog module @<:@default=no@:>@])],
@@ -828,6 +841,7 @@ AC_CONFIG_FILES([Makefile \
plugins/omprog/Makefile \
plugins/omstdout/Makefile \
plugins/imfile/Makefile \
+ plugins/imptcp/Makefile \
plugins/imrelp/Makefile \
plugins/imdiag/Makefile \
plugins/omtesting/Makefile \
@@ -857,6 +871,7 @@ echo " custom module 1 will be built: $enable_cust1"
echo
echo "---{ input plugins }---"
echo " Klog functionality enabled: $enable_klog ($os_type)"
+echo " plain tcp input module enabled: $enable_imptcp"
echo " imdiag enabled: $enable_imdiag"
echo " file input module enabled: $enable_imfile"
echo " input template module will be compiled: $enable_imtemplate"
diff --git a/doc/Makefile.am b/doc/Makefile.am
index ca2ee71c..7ba86ad4 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -37,6 +37,7 @@ html_files = \
omlibdbi.html \
imfile.html \
imtcp.html \
+ imptcp.html \
imgssapi.html \
imrelp.html \
imuxsock.html \
diff --git a/doc/imptcp.html b/doc/imptcp.html
new file mode 100644
index 00000000..913563a5
--- /dev/null
+++ b/doc/imptcp.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="Content-Language" content="en">
+<title>Plain TCP Syslog Input Module (imptcp)</title></head>
+<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
+<h1>Plain TCP Syslog Input Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; imptcp</b></p>
+<p><b>Available since: </b>4.7.3+, 5.5.8+?
+<p><b>Author: </b>Rainer Gerhards
+&lt;rgerhards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>Provides the ability to receive syslog messages via plain TCP syslog.
+This is a specialised input plugin tailored for high performance on Linux. It will
+probably not run on any other platform. Also, it does no provide TLS services.
+Encryption can be provided by using <a href="rsyslog_stunnel.html">stunnel</a>.
+<p>This module has no limit on the number of listeners and sessions that can be used.
+<p>Multiple receivers may be configured by
+specifying $InputPTCPServerRun multiple times.
+</p>
+<p><b>Configuration Directives</b>:</p>
+<p>This plugin has config directives similar named as imtcp, but they all have <b>P</b>TCP in
+their name instead of just TCP. Note that only a subset of the parameters are supported.
+<ul>
+<li>$InputPTCPServerAddtlFrameDelimiter &lt;Delimiter&gt;<br>
+<b>CURRENTLY DISABLED</b><br>
+This directive permits to specify an additional frame delimiter for plain tcp syslog.
+The industry-standard specifies using the LF character as frame delimiter. Some vendors,
+notable Juniper in their NetScreen products, use an invalid frame delimiter, in Juniper's
+case the NUL character. This directive permits to specify the ASCII value of the delimiter
+in question. Please note that this does not guarantee that all wrong implementations can
+be cured with this directive. It is not even a sure fix with all versions of NetScreen,
+as I suggest the NUL character is the effect of a (common) coding error and thus will
+probably go away at some time in the future. But for the time being, the value 0 can
+probably be used to make rsyslog handle NetScreen's invalid syslog/tcp framing.
+For additional information, see this
+<a href="http://kb.monitorware.com/problem-with-netscreen-log-t1652.html">forum thread</a>.
+<br><b>If this doesn't work for you, please do not blame the rsyslog team. Instead file
+a bug report with Juniper!</b>
+<br>Note that a similar, but worse, issue exists with Cisco's IOS implementation. They do
+not use any framing at all. This is confirmed from Cisco's side, but there seems to be
+very limited interest in fixing this issue. This directive <b>can not</b> fix the Cisco bug.
+That would require much more code changes, which I was unable to do so far. Full details
+can be found at the <a href="http://www.rsyslog.com/Article321.phtml">Cisco tcp syslog anomaly</a>
+page.
+<li>$InputPTCPServerNotifyOnConnectionClose [on/<b>off</b>]<br>
+<b>CURRENTLY DISABLED</b><br>
+instructs imptcp to emit a message if the remote peer closes a connection.<br>
+<b>Important:</b> This directive is global to all listeners and must be given right
+after loading imptcp, otherwise it may have no effect.</li>
+<li>$InputPTCPServerRun &lt;port&gt;<br>
+Starts a TCP server on selected port</li>
+<li>$InputPTCPServerInputName &lt;name&gt;<br>
+Sets a name for the inputname property. If no name is set "imptcp" is used by default. Setting a
+name is not strictly necessary, but can be useful to apply filtering based on which input
+the message was received from.
+<li>$InputPTCPServerBindRuleset &lt;name&gt;<br>
+Binds specified ruleset to next server defined.
+<li>$InputPTCPServerListenIP &lt;name&gt;<br>
+On multi-homed machines, specifies to which local address the next listerner should
+be bound.
+</ul>
+<b>Caveats/Known Bugs:</b>
+<ul>
+<li>module always binds to all interfaces</li>
+</ul>
+<p><b>Sample:</b></p>
+<p>This sets up a TCP server on port 514:<br>
+</p>
+<textarea rows="15" cols="60">$ModLoad imptcp #
+needs to be done just once
+$InputPTCPServerRun 514
+</textarea>
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
+[<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a>
+project.<br>
+Copyright &copy; 2010 by <a href="http://www.gerhards.net/rainer">Rainer
+Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>.
+Released under the GNU GPL version 3 or higher.</font></p>
+</body></html>
diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html
index 675b8bb3..19f69da6 100644
--- a/doc/rsyslog_conf_modules.html
+++ b/doc/rsyslog_conf_modules.html
@@ -27,7 +27,8 @@ to message generators.
<li><a href="imfile.html">imfile</a> -&nbsp; input module for text files</li>
<li><a href="imrelp.html">imrelp</a> - RELP input module</li>
<li>imudp - udp syslog message input</li>
-<li><a href="imtcp.html">imtcp</a> - input plugin for plain tcp syslog</li>
+<li><a href="imtcp.html">imtcp</a> - input plugin for tcp syslog</li>
+<li><a href="imptcp.html">imptcp</a> - input plugin for plain tcp syslog (no TLS but faster)</li>
<li><a href="imgssapi.html">imgssapi</a> - input plugin for plain tcp and GSS-enabled syslog</li>
<li>immark - support for mark messages</li>
<li><a href="imklog.html">imklog</a> - kernel logging</li>
diff --git a/plugins/imptcp/Makefile.am b/plugins/imptcp/Makefile.am
new file mode 100644
index 00000000..bfacc884
--- /dev/null
+++ b/plugins/imptcp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imptcp.la
+
+imptcp_la_SOURCES = imptcp.c
+imptcp_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+imptcp_la_LDFLAGS = -module -avoid-version
+imptcp_la_LIBADD =
diff --git a/plugins/imptcp/imptcp.c b/plugins/imptcp/imptcp.c
new file mode 100644
index 00000000..5aeb0192
--- /dev/null
+++ b/plugins/imptcp/imptcp.c
@@ -0,0 +1,1146 @@
+/* imptcp.c
+ * This is a native implementation of plain tcp. It is intentionally
+ * duplicate work (imtcp). The intent is to gain very fast and simple
+ * native ptcp support, utilizing the best interfaces Linux (no cross-
+ * platform intended!) has to offer.
+ *
+ * Note that in this module we try out some new naming conventions,
+ * so it may look a bit "different" from the other modules. We are
+ * investigating if removing prefixes can help make code more readable.
+ *
+ * File begun on 2010-08-10 by RGerhards
+ *
+ * Copyright 2007-2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include <stdio.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>
+#include <sys/epoll.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "cfsysline.h"
+#include "prop.h"
+#include "dirty.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+#include "glbl.h"
+#include "prop.h"
+#include "errmsg.h"
+#include "srUtils.h"
+#include "datetime.h"
+#include "ruleset.h"
+#include "msg.h"
+#include "net.h" /* for permittedPeers, may be removed when this is removed */
+//#include "tcpsrv.h" /* NOTE: we use some defines from this module -- TODO: re-think! */
+
+MODULE_TYPE_INPUT
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(ruleset)
+
+
+
+/* config settings */
+typedef struct configSettings_s {
+ int bEmitMsgOnClose; /* emit an informational message on close by remote peer */
+ int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */
+ uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */
+ uchar *lstnIP; /* which IP we should listen on? */
+ ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+} configSettings_t;
+
+static configSettings_t cs;
+
+/* data elements describing our running config */
+typedef struct ptcpsrv_s ptcpsrv_t;
+typedef struct ptcplstn_s ptcplstn_t;
+typedef struct ptcpsess_s ptcpsess_t;
+typedef struct epolld_s epolld_t;
+
+/* the ptcp server (listener) object
+ * Note that the object contains support for forming a linked list
+ * of them. It does not make sense to do this seperately.
+ */
+struct ptcpsrv_s {
+ ptcpsrv_t *pNext; /* linked list maintenance */
+ uchar *port; /* Port to listen to */
+ uchar *lstnIP; /* which IP we should listen on? */
+ int bEmitMsgOnClose;
+ int iAddtlFrameDelim;
+ uchar *pszInputName;
+ prop_t *pInputName; /* InputName in (fast to process) property format */
+ ruleset_t *pRuleset;
+ ptcplstn_t *pLstn; /* root of our listeners */
+ ptcpsess_t *pSess; /* root of our sessions */
+};
+
+/* the ptcp session object. Describes a single active session.
+ * includes support for doubly-linked list.
+ */
+struct ptcpsess_s {
+ ptcpsrv_t *pSrv; /* our server */
+ ptcpsess_t *prev, *next;
+ int sock;
+ epolld_t *epd;
+//--- from tcps_sess.h
+ int iMsg; /* index of next char to store in msg */
+ int bAtStrtOfFram; /* are we at the very beginning of a new frame? */
+ enum {
+ eAtStrtFram,
+ eInOctetCnt,
+ eInMsg
+ } inputState; /* our current state */
+ int iOctetsRemain; /* Number of Octets remaining in message */
+ TCPFRAMINGMODE eFraming;
+ uchar *pMsg; /* message (fragment) received */
+ prop_t *peerName; /* host name we received messages from */
+ prop_t *peerIP;
+//--- END from tcps_sess.h
+};
+
+
+/* the ptcp listener object. Describes a single active listener.
+ */
+struct ptcplstn_s {
+ ptcpsrv_t *pSrv; /* our server */
+ ptcplstn_t *prev, *next;
+ int sock;
+ epolld_t *epd;
+};
+
+
+/* type of object stored in epoll descriptor */
+typedef enum {
+ epolld_lstn,
+ epolld_sess
+} epolld_type_t;
+
+/* an epoll descriptor. contains all information necessary to process
+ * the result of epoll.
+ */
+struct epolld_s {
+ epolld_type_t typ;
+ void *ptr;
+ struct epoll_event ev;
+};
+
+
+/* global data */
+//static permittedPeers_t *pPermPeersRoot = NULL;
+static ptcpsrv_t *pSrvRoot = NULL;
+static int epollfd = -1; /* (sole) descriptor for epoll */
+static int iMaxLine; /* maximum size of a single message */
+
+/* forward definitions */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+static rsRetVal addLstn(ptcpsrv_t *pSrv, int sock);
+
+
+/* some simple constructors/destructors */
+static void
+destructSess(ptcpsess_t *pSess)
+{
+ free(pSess->pMsg);
+ free(pSess->epd);
+ prop.Destruct(&pSess->peerName);
+ prop.Destruct(&pSess->peerIP);
+ /* TODO: make these inits compile-time switch depending: */
+ pSess->pMsg = NULL;
+ pSess->epd = NULL;
+ free(pSess);
+}
+
+static void
+destructSrv(ptcpsrv_t *pSrv)
+{
+ prop.Destruct(&pSrv->pInputName);
+ free(pSrv->port);
+ free(pSrv);
+}
+
+/****************************************** TCP SUPPORT FUNCTIONS ***********************************/
+/* We may later think about moving this into a helper library again. But the whole point
+ * so far was to keep everything related close togehter. -- rgerhards, 2010-08-10
+ */
+
+
+/* Start up a server. That means all of its listeners are created.
+ * Does NOT yet accept/process any incoming data (but binds ports). Hint: this
+ * code is to be executed before dropping privileges.
+ */
+static rsRetVal
+startupSrv(ptcpsrv_t *pSrv)
+{
+ DEFiRet;
+ int iSessMax = 200; /* TODO: Make configurable or remove? */
+ int error, maxs, on = 1;
+ int sock = -1;
+ int numSocks;
+ int sockflags;
+ struct addrinfo hints, *res = NULL, *r;
+ uchar *lstnIP;
+
+ lstnIP = pSrv->lstnIP == NULL ? UCHAR_CONSTANT("") : pSrv->lstnIP;
+
+ dbgprintf("imptcp creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = glbl.GetDefPFFamily();
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo((char*)pSrv->lstnIP, (char*) pSrv->port, &hints, &res);
+ if(error) {
+ dbgprintf("error %d querying server '%s', port '%s'\n", error, pSrv->lstnIP, pSrv->port);
+ 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);
+ sock = -1;
+ 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);
+ sock = -1;
+ 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);
+ sock = -1;
+ 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(errno, NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
+ close(sock);
+ sock = -1;
+ 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);
+ sock = -1;
+ 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);
+ sock = -1;
+ continue;
+ }
+ }
+
+ /* if we reach this point, we were able to obtain a valid socket, so we can
+ * create our listener object. -- rgerhards, 2010-08-10
+ */
+ CHKiRet(addLstn(pSrv, sock));
+ ++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(sock != -1)
+ close(sock);
+ }
+
+ 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
+getPeerNames(prop_t **peerName, prop_t **peerIP, struct sockaddr *pAddr)
+{
+ int error;
+ uchar szIP[NI_MAXHOST] = "";
+ uchar szHname[NI_MAXHOST] = "";
+ struct addrinfo hints, *res;
+
+ DEFiRet;
+
+ 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. */
+ CHKiRet(prop.Construct(peerName));
+ CHKiRet(prop.SetString(*peerName, szHname, ustrlen(szHname)));
+ CHKiRet(prop.ConstructFinalize(*peerName));
+ CHKiRet(prop.Construct(peerIP));
+ CHKiRet(prop.SetString(*peerIP, szIP, ustrlen(szIP)));
+ CHKiRet(prop.ConstructFinalize(*peerIP));
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* accept an incoming connection request
+ * rgerhards, 2008-04-22
+ */
+static rsRetVal
+AcceptConnReq(int sock, int *newSock, prop_t **peerName, prop_t **peerIP)
+{
+ int sockflags;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ int iNewSock = -1;
+
+ DEFiRet;
+
+ iNewSock = accept(sock, (struct sockaddr*) &addr, &addrlen);
+ if(iNewSock < 0) {
+ ABORT_FINALIZE(RS_RET_ACCEPT_ERR);
+ }
+
+ CHKiRet(getPeerNames(peerName, peerIP, (struct sockaddr*) &addr));
+
+ /* set the new socket to non-blocking IO */
+ 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);
+ }
+
+ *newSock = iNewSock;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* the close may be redundant, but that doesn't hurt... */
+ close(iNewSock);
+ }
+
+ RETiRet;
+}
+
+
+/* This is a helper for submitting the message to the rsyslog core.
+ * It does some common processing, including resetting the various
+ * state variables to a "processed" state.
+ * Note that this function is also called if we had a buffer overflow
+ * due to a too-long message. So far, there is no indication this
+ * happened and it may be worth thinking about different handling
+ * of this case (what obviously would require a change to this
+ * function or some related code).
+ * rgerhards, 2009-04-23
+ * EXTRACT from tcps_sess.c
+ */
+static rsRetVal
+doSubmitMsg(ptcpsess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ msg_t *pMsg;
+ DEFiRet;
+
+ if(pThis->iMsg == 0) {
+ DBGPRINTF("discarding zero-sized message\n");
+ FINALIZE;
+ }
+
+ /* we now create our own message object and submit it to the queue */
+ CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
+ MsgSetRawMsg(pMsg, (char*)pThis->pMsg, pThis->iMsg);
+ MsgSetInputName(pMsg, pThis->pSrv->pInputName);
+ MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
+ pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
+ pMsg->bParseHOSTNAME = 1;
+ MsgSetRcvFrom(pMsg, pThis->peerName);
+ CHKiRet(MsgSetRcvFromIP(pMsg, pThis->peerIP));
+ MsgSetRuleset(pMsg, pThis->pSrv->pRuleset);
+
+ if(pMultiSub == NULL) {
+ CHKiRet(submitMsg(pMsg));
+ } else {
+ pMultiSub->ppMsgs[pMultiSub->nElem++] = pMsg;
+ if(pMultiSub->nElem == pMultiSub->maxElem)
+ CHKiRet(multiSubmitMsg(pMultiSub));
+ }
+
+
+finalize_it:
+ /* reset status variables */
+ pThis->bAtStrtOfFram = 1;
+ pThis->iMsg = 0;
+
+ RETiRet;
+}
+
+
+/* process the data received. As TCP is stream based, we need to process the
+ * data inside a state machine. The actual data received is passed in byte-by-byte
+ * from DataRcvd, and this function here compiles messages from them and submits
+ * the end result to the queue. Introducing this function fixes a long-term bug ;)
+ * rgerhards, 2008-03-14
+ * EXTRACT from tcps_sess.c
+ */
+static inline rsRetVal
+processDataRcvd(ptcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ DEFiRet;
+
+ if(pThis->inputState == eAtStrtFram) {
+ if(isdigit((int) c)) {
+ pThis->inputState = eInOctetCnt;
+ pThis->iOctetsRemain = 0;
+ pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
+ } else {
+ pThis->inputState = eInMsg;
+ pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
+ }
+ }
+
+ if(pThis->inputState == eInOctetCnt) {
+ if(isdigit(c)) {
+ pThis->iOctetsRemain = pThis->iOctetsRemain * 10 + c - '0';
+ } else { /* done with the octet count, so this must be the SP terminator */
+ dbgprintf("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
+ if(c != ' ') {
+ errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
+ "delimiter is not SP but has ASCII value %d.\n", c);
+ }
+ if(pThis->iOctetsRemain < 1) {
+ /* TODO: handle the case where the octet count is 0! */
+ dbgprintf("Framing Error: invalid octet count\n");
+ errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
+ "invalid octet count %d.\n", pThis->iOctetsRemain);
+ } else if(pThis->iOctetsRemain > iMaxLine) {
+ /* while we can not do anything against it, we can at least log an indication
+ * that something went wrong) -- rgerhards, 2008-03-14
+ */
+ dbgprintf("truncating message with %d octets - max msg size is %d\n",
+ pThis->iOctetsRemain, iMaxLine);
+ errmsg.LogError(0, NO_ERRCODE, "received oversize message: size is %d bytes, "
+ "max msg size is %d, truncating...\n", pThis->iOctetsRemain, iMaxLine);
+ }
+ pThis->inputState = eInMsg;
+ }
+ } else {
+ assert(pThis->inputState == eInMsg);
+ if(pThis->iMsg >= iMaxLine) {
+ /* emergency, we now need to flush, no matter if we are at end of message or not... */
+ dbgprintf("error: message received is larger than max msg size, we split it\n");
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ /* we might think if it is better to ignore the rest of the
+ * message than to treat it as a new one. Maybe this is a good
+ * candidate for a configuration parameter...
+ * rgerhards, 2006-12-04
+ */
+ }
+
+ if(( (c == '\n')
+ //|| ((pThis->pSrv->addtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->addtlFrameDelim))
+ ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ pThis->inputState = eAtStrtFram;
+ } else {
+ /* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
+ * If we have a message that is larger than the max msg size, we truncate it. This is the best
+ * we can do in light of what the engine supports. -- rgerhards, 2008-03-14
+ */
+ if(pThis->iMsg < iMaxLine) {
+ *(pThis->pMsg + pThis->iMsg++) = c;
+ }
+ }
+
+ if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
+ /* do we need to find end-of-frame via octet counting? */
+ pThis->iOctetsRemain--;
+ if(pThis->iOctetsRemain < 1) {
+ /* we have end of frame! */
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ pThis->inputState = eAtStrtFram;
+ }
+ }
+ }
+
+ RETiRet;
+}
+
+
+/* Processes the data received via a TCP session. If there
+ * is no other way to handle it, data is discarded.
+ * Input parameter data is the data received, iLen is its
+ * len as returned from recv(). iLen must be 1 or more (that
+ * is errors must be handled by caller!). iTCPSess must be
+ * the index of the TCP session that received the data.
+ * rgerhards 2005-07-04
+ * And another change while generalizing. We now return either
+ * RS_RET_OK, which means the session should be kept open
+ * or anything else, which means it must be closed.
+ * rgerhards, 2008-03-01
+ * As a performance optimization, we pick up the timestamp here. Acutally,
+ * this *is* the *correct* reception step for all the data we received, because
+ * we have just received a bunch of data! -- rgerhards, 2009-06-16
+ * EXTRACT from tcps_sess.c
+ */
+#define NUM_MULTISUB 1024
+static rsRetVal
+DataRcvd(ptcpsess_t *pThis, char *pData, size_t iLen)
+{
+ multi_submit_t multiSub;
+ msg_t *pMsgs[NUM_MULTISUB];
+ struct syslogTime stTime;
+ time_t ttGenTime;
+ char *pEnd;
+ DEFiRet;
+
+ assert(pData != NULL);
+ assert(iLen > 0);
+
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ multiSub.ppMsgs = pMsgs;
+ multiSub.maxElem = NUM_MULTISUB;
+ multiSub.nElem = 0;
+
+ /* We now copy the message to the session buffer. */
+ pEnd = pData + iLen; /* this is one off, which is intensional */
+
+ while(pData < pEnd) {
+ CHKiRet(processDataRcvd(pThis, *pData++, &stTime, ttGenTime, &multiSub));
+ }
+
+ if(multiSub.nElem > 0) {
+ /* submit anything that was not yet submitted */
+ CHKiRet(multiSubmitMsg(&multiSub));
+ }
+
+finalize_it:
+ RETiRet;
+}
+#undef NUM_MULTISUB
+
+
+/****************************************** --END-- TCP SUPPORT FUNCTIONS ***********************************/
+
+
+static inline void
+initConfigSettings(void)
+{
+ cs.bEmitMsgOnClose = 0;
+ //cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ cs.pszInputName = NULL;
+ cs.pRuleset = NULL;
+ cs.lstnIP = NULL;
+}
+
+
+/* add socket to the epoll set
+ */
+static inline rsRetVal
+addEPollSock(epolld_type_t typ, void *ptr, int sock, epolld_t **pEpd)
+{
+ DEFiRet;
+ epolld_t *epd = NULL;
+
+ CHKmalloc(epd = malloc(sizeof(epolld_t)));
+ epd->typ = typ;
+ epd->ptr = ptr;
+ *pEpd = epd;
+ epd->ev.events = EPOLLIN|EPOLLET;
+ epd->ev.data.ptr = (void*) epd;
+
+ if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &(epd->ev)) != 0) {
+ char errStr[1024];
+ int eno = errno;
+ errmsg.LogError(0, RS_RET_EPOLL_CTL_FAILED, "os error (%d) during epoll ADD: %s",
+ eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ ABORT_FINALIZE(RS_RET_EPOLL_CTL_FAILED);
+ }
+
+ DBGPRINTF("imptcp: added socket %d to epoll[%d] set\n", sock, epollfd);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ free(epd);
+ }
+ RETiRet;
+}
+
+
+/* remove a socket from the epoll set. Note that the epd parameter
+ * is not really required -- it is used to satisfy older kernels where
+ * epoll_ctl() required a non-NULL pointer even though the ptr is never used.
+ * For simplicity, we supply the same pointer we had when we created the
+ * event (it's simple because we have it at hand).
+ */
+static inline rsRetVal
+removeEPollSock(int sock, epolld_t *epd)
+{
+ DEFiRet;
+
+ DBGPRINTF("imptcp: removing socket %d from epoll[%d] set\n", sock, epollfd);
+
+ if(epoll_ctl(epollfd, EPOLL_CTL_DEL, sock, &(epd->ev)) != 0) {
+ char errStr[1024];
+ int eno = errno;
+ errmsg.LogError(0, RS_RET_EPOLL_CTL_FAILED, "os error (%d) during epoll DEL: %s",
+ eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ ABORT_FINALIZE(RS_RET_EPOLL_CTL_FAILED);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* add a listener to the server
+ */
+static rsRetVal
+addLstn(ptcpsrv_t *pSrv, int sock)
+{
+ DEFiRet;
+ ptcplstn_t *pLstn;
+
+ CHKmalloc(pLstn = malloc(sizeof(ptcplstn_t)));
+ pLstn->pSrv = pSrv;
+ pLstn->sock = sock;
+
+ /* add to start of server's listener list */
+ pLstn->prev = NULL;
+ pLstn->next = pSrv->pLstn;
+ pSrv->pLstn = pLstn;
+
+ iRet = addEPollSock(epolld_lstn, pLstn, sock, &pLstn->epd);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* add a session to the server
+ */
+static rsRetVal
+addSess(ptcpsrv_t *pSrv, int sock, prop_t *peerName, prop_t *peerIP)
+{
+ DEFiRet;
+ ptcpsess_t *pSess = NULL;
+
+ CHKmalloc(pSess = malloc(sizeof(ptcpsess_t)));
+ CHKmalloc(pSess->pMsg = malloc(iMaxLine * sizeof(uchar)));
+ pSess->pSrv = pSrv;
+ pSess->sock = sock;
+ pSess->inputState = eAtStrtFram;
+ pSess->iMsg = 0;
+ pSess->bAtStrtOfFram = 1;
+ pSess->peerName = peerName;
+ pSess->peerIP = peerIP;
+
+ /* add to start of server's listener list */
+ pSess->prev = NULL;
+ pSess->next = pSrv->pSess;
+ pSrv->pSess = pSess;
+
+ iRet = addEPollSock(epolld_sess, pSess, sock, &pSess->epd);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* close/remove a session
+ * NOTE: we must first remove the fd from the epoll set and then close it -- else we
+ * get an error "bad file descriptor" from epoll.
+ */
+static rsRetVal
+closeSess(ptcpsess_t *pSess)
+{
+ int sock;
+ DEFiRet;
+
+ sock = pSess->sock;
+ CHKiRet(removeEPollSock(sock, pSess->epd));
+ close(sock);
+
+ /* finally unlink session from structures */
+ if(pSess->next != NULL)
+ pSess->next->prev = pSess->prev;
+ if(pSess->prev == NULL) {
+ /* need to update root! */
+ pSess->pSrv->pSess = pSess->next;
+ } else {
+ pSess->prev->next = pSess->next;
+ }
+
+ /* unlinked, now remove structure */
+ destructSess(pSess);
+
+finalize_it:
+ DBGPRINTF("imtcp: session on socket %d closed with iRet %d.\n", sock, iRet);
+ RETiRet;
+}
+
+
+#if 0
+/* set permitted peer -- rgerhards, 2008-05-19
+ */
+static rsRetVal
+setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID)
+{
+ DEFiRet;
+ CHKiRet(net.AddPermittedPeer(&pPermPeersRoot, pszID));
+ free(pszID); /* no longer needed, but we need to free as of interface def */
+finalize_it:
+ RETiRet;
+}
+#endif
+
+
+/* accept a new ruleset to bind. Checks if it exists and complains, if not */
+static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.GetRuleset(&pRuleset, pszName);
+ if(localRet == RS_RET_NOT_FOUND) {
+ errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
+ }
+ CHKiRet(localRet);
+ cs.pRuleset = pRuleset;
+ DBGPRINTF("imptcp current bind ruleset %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+
+static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ ptcpsrv_t *pSrv;
+
+ CHKmalloc(pSrv = malloc(sizeof(ptcpsrv_t)));
+ pSrv->pSess = NULL;
+ pSrv->pLstn = NULL;
+ pSrv->bEmitMsgOnClose = cs.bEmitMsgOnClose;
+ pSrv->port = pNewVal;
+ //pSrv->iAddtlFrameDelim = cs.iAddtlFrameDelim;
+ cs.pszInputName = NULL; /* moved over to pSrv, we do not own */
+ pSrv->lstnIP = cs.lstnIP;
+ cs.lstnIP = NULL; /* moved over to pSrv, we do not own */
+ pSrv->pRuleset = cs.pRuleset;
+ pSrv->pszInputName = (cs.pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : cs.pszInputName;
+ CHKiRet(prop.Construct(&pSrv->pInputName));
+ CHKiRet(prop.SetString(pSrv->pInputName, pSrv->pszInputName, ustrlen(pSrv->pszInputName)));
+ CHKiRet(prop.ConstructFinalize(pSrv->pInputName));
+
+ /* add to linked list */
+ pSrv->pNext = pSrvRoot;
+ pSrvRoot = pSrv;
+
+ /* all config vars are auto-reset -- this also is very useful with the
+ * new config format effort (v6).
+ */
+ resetConfigVariables(NULL, NULL);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
+ }
+ RETiRet;
+}
+
+
+/* start up all listeners
+ * This is a one-time stop once the module is set to start.
+ */
+static inline rsRetVal
+startupServers()
+{
+ DEFiRet;
+ ptcpsrv_t *pSrv;
+
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ DBGPRINTF("Starting up ptcp server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName);
+ startupSrv(pSrv);
+ pSrv = pSrv->pNext;
+ }
+
+ RETiRet;
+}
+
+
+/* process new activity on listener. This means we need to accept a new
+ * connection.
+ */
+static inline rsRetVal
+lstnActivity(ptcplstn_t *pLstn)
+{
+ int newSock;
+ prop_t *peerName;
+ prop_t *peerIP;
+ DEFiRet;
+
+ DBGPRINTF("imptcp: new connection on listen socket %d\n", pLstn->sock);
+ CHKiRet(AcceptConnReq(pLstn->sock, &newSock, &peerName, &peerIP));
+ CHKiRet(addSess(pLstn->pSrv, newSock, peerName, peerIP));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* process new activity on session. This means we need to accept data
+ * or close the session.
+ */
+static inline rsRetVal
+sessActivity(ptcpsess_t *pSess)
+{
+ int lenRcv;
+ int lenBuf;
+ char rcvBuf[4096];
+ DEFiRet;
+
+ DBGPRINTF("imptcp: new activity on session socket %d\n", pSess->sock);
+
+ lenBuf = sizeof(rcvBuf);
+ lenRcv = recv(pSess->sock, rcvBuf, lenBuf, 0);
+
+ if(lenRcv > 0) {
+ /* have data, process it */
+ DBGPRINTF("imtcp: data(%d) on socket %d: %s\n", lenBuf, pSess->sock, rcvBuf);
+ CHKiRet(DataRcvd(pSess, rcvBuf, lenRcv));
+ } else if (lenRcv == 0) {
+ /* session was closed, do clean-up */
+ CHKiRet(closeSess(pSess));
+ } else {
+ DBGPRINTF("imtcp: error on session socket %d - closed.\n", pSess->sock);
+ closeSess(pSess); /* try clean-up by dropping session */
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+ int i;
+ int nfds;
+ struct epoll_event events[1];
+ epolld_t *epd;
+CODESTARTrunInput
+ DBGPRINTF("imptcp now beginning to process input data\n");
+ /* v5 TODO: consentual termination mode */
+ while(1) {
+ DBGPRINTF("imptcp going on epoll_wait\n");
+ nfds = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1);
+ for(i = 0 ; i < nfds ; ++i) { /* support for larger batches (later, TODO) */
+ epd = (epolld_t*) events[i].data.ptr;
+ switch(epd->typ) {
+ case epolld_lstn:
+ lstnActivity((ptcplstn_t *) epd->ptr);
+ break;
+ case epolld_sess:
+ sessActivity((ptcpsess_t *) epd->ptr);
+ break;
+ default:
+ errmsg.LogError(0, RS_RET_INTERNAL_ERROR,
+ "error: invalid epolld_type_t %d after epoll", epd->typ);
+ break;
+ }
+ }
+ }
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ /* first apply some config settings */
+ //net.PrintAllowedSenders(2); /* TCP */
+ iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
+
+ if(pSrvRoot == NULL) {
+ errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "error: no ptcp server defined, module can not run.");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+ if((epollfd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+ errmsg.LogError(0, RS_RET_EPOLL_CR_FAILED, "error: epoll_create() failed");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+ /* start up servers, but do not yet read input data */
+ CHKiRet(startupServers());
+ DBGPRINTF("imptcp started up, but not yet receiving data\n");
+finalize_it:
+ENDwillRun
+
+
+/* completely shut down a server, that means closing all of its
+ * listeners and sessions.
+ */
+static inline void
+shutdownSrv(ptcpsrv_t *pSrv)
+{
+ ptcplstn_t *pLstn, *lstnDel;
+ ptcpsess_t *pSess, *sessDel;
+
+ /* listeners */
+ pLstn = pSrv->pLstn;
+ while(pLstn != NULL) {
+ close(pLstn->sock);
+ lstnDel = pLstn;
+ pLstn = pLstn->next;
+ DBGPRINTF("imptcp shutdown listen socket %d\n", lstnDel->sock);
+ free(lstnDel->epd);
+ free(lstnDel);
+ }
+
+ /* sessions */
+ pSess = pSrv->pSess;
+ while(pSess != NULL) {
+ close(pSess->sock);
+ sessDel = pSess;
+ pSess = pSess->next;
+ DBGPRINTF("imptcp shutdown session socket %d\n", sessDel->sock);
+ destructSess(sessDel);
+ }
+}
+
+
+BEGINafterRun
+ ptcpsrv_t *pSrv, *srvDel;
+CODESTARTafterRun
+ /* do cleanup here */
+ //net.clearAllowedSenders(UCHAR_CONSTANT("TCP"));
+ /* we need to close everything that is still open */
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ srvDel = pSrv;
+ pSrv = pSrv->pNext;
+ shutdownSrv(srvDel);
+ destructSrv(srvDel);
+ }
+
+ close(epollfd);
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+#if 0
+ if(pPermPeersRoot != NULL) {
+ net.DestructPermittedPeers(&pPermPeersRoot);
+ }
+#endif
+
+ /* release objects we used */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ENDmodExit
+
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ cs.bEmitMsgOnClose = 0;
+ //cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ free(cs.pszInputName);
+ cs.pszInputName = NULL;
+ free(cs.lstnIP);
+ cs.lstnIP = NULL;
+ return RS_RET_OK;
+}
+
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ initConfigSettings();
+ /* request objects we use */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverrun"), 0, eCmdHdlrGetWord,
+ addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpservernotifyonconnectionclose"), 0,
+ eCmdHdlrBinary, NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID));
+ //CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverstreamdriverpermittedpeer"), 0,
+ //eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
+ NULL, &cs.iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverinputname"), 0,
+ eCmdHdlrGetWord, NULL, &cs.pszInputName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverlistenip"), 0,
+ eCmdHdlrGetWord, NULL, &cs.lstnIP, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverbindruleset"), 0,
+ eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+
+/* vim:set ai:
+ */
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 8979893a..907b9c1a 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -360,6 +360,10 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_VAR_NOT_FOUND = -2142, /**< variable not found */
RS_RET_EMPTY_MSG = -2143, /**< provided (raw) MSG is empty */
RS_RET_PEER_CLOSED_CONN = -2144, /**< remote peer closed connection (information, no error) */
+ RS_RET_NO_LSTN_DEFINED = -2172, /**< no listener defined (e.g. inside an input module) */
+ RS_RET_EPOLL_CR_FAILED = -2173, /**< epoll_create() failed */
+ RS_RET_EPOLL_CTL_FAILED = -2174, /**< epoll_ctl() failed */
+ RS_RET_INTERNAL_ERROR = -2175, /**< rsyslogd internal error, unexpected code path reached */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/tools/syslogd.c b/tools/syslogd.c
index a03dcf0e..18e0c98f 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -371,7 +371,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
/* hardcoded standard templates (used for defaults) */
-static uchar template_DebugFormat[] = "\"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%', HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\nrawmsg: '%rawmsg%'\n\n\"";
+static uchar template_DebugFormat[] = "\"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%', HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\ninputname: %inputname% rawmsg: '%rawmsg%'\n\n\"";
static uchar template_SyslogProtocol23Format[] = "\"<%PRI%>1 %TIMESTAMP:::date-rfc3339% %HOSTNAME% %APP-NAME% %PROCID% %MSGID% %STRUCTURED-DATA% %msg%\n\"";
static uchar template_TraditionalFileFormat[] = "\"%TIMESTAMP% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n\"";
static uchar template_FileFormat[] = "\"%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n\"";