diff options
-rw-r--r-- | .gitignore (renamed from .cvsignore) | 16 | ||||
-rw-r--r-- | ChangeLog | 163 | ||||
-rw-r--r-- | Makefile.am | 153 | ||||
-rw-r--r-- | action.c | 18 | ||||
-rw-r--r-- | configure.ac | 88 | ||||
-rw-r--r-- | contrib/gnutls/ca-key.pem | 15 | ||||
-rw-r--r-- | contrib/gnutls/ca.pem | 15 | ||||
-rw-r--r-- | contrib/gnutls/cert.pem | 16 | ||||
-rw-r--r-- | contrib/gnutls/key.pem | 15 | ||||
-rw-r--r-- | dirty.h | 81 | ||||
-rw-r--r-- | doc/.cvsignore | 2 | ||||
-rw-r--r-- | doc/Makefile.am | 20 | ||||
-rw-r--r-- | doc/features.html | 39 | ||||
-rw-r--r-- | doc/im3195.html | 46 | ||||
-rw-r--r-- | doc/imtcp.html | 13 | ||||
-rw-r--r-- | doc/manual.html | 5 | ||||
-rw-r--r-- | doc/netstream.html | 21 | ||||
-rw-r--r-- | doc/ns_gtls.html | 59 | ||||
-rw-r--r-- | doc/ns_ptcp.html | 16 | ||||
-rw-r--r-- | doc/professional_support.html | 55 | ||||
-rw-r--r-- | doc/property_replacer.html | 41 | ||||
-rw-r--r-- | doc/rsyslog_conf.html | 24 | ||||
-rw-r--r-- | doc/rsyslog_mysql.html | 2 | ||||
-rw-r--r-- | doc/rsyslog_ng_comparison.html | 36 | ||||
-rw-r--r-- | doc/rsyslog_secure_tls.html | 127 | ||||
-rw-r--r-- | doc/rsyslog_stunnel.html | 488 | ||||
-rw-r--r-- | doc/rsyslog_tls.html | 307 | ||||
-rw-r--r-- | doc/src/queueWorkerLogic.dia (renamed from doc/queueWorkerLogic.dia) | bin | 3334 -> 3334 bytes | |||
-rw-r--r-- | doc/src/tls.dia | bin | 0 -> 4656 bytes | |||
-rw-r--r-- | doc/src/tls_cert.dia | bin | 0 -> 2531 bytes | |||
-rw-r--r-- | doc/src/tls_cert_100.dia | bin | 0 -> 1885 bytes | |||
-rw-r--r-- | doc/src/tls_cert_ca.dia | bin | 0 -> 1230 bytes | |||
-rw-r--r-- | doc/status.html | 24 | ||||
-rw-r--r-- | doc/tls_cert.jpg | bin | 0 -> 68335 bytes | |||
-rw-r--r-- | doc/tls_cert_100.jpg | bin | 0 -> 16607 bytes | |||
-rw-r--r-- | doc/tls_cert_ca.html | 168 | ||||
-rw-r--r-- | doc/tls_cert_ca.jpg | bin | 0 -> 9635 bytes | |||
-rw-r--r-- | doc/tls_cert_client.html | 91 | ||||
-rw-r--r-- | doc/tls_cert_errmsgs.html | 103 | ||||
-rw-r--r-- | doc/tls_cert_machine.html | 172 | ||||
-rw-r--r-- | doc/tls_cert_scenario.html | 63 | ||||
-rw-r--r-- | doc/tls_cert_server.html | 118 | ||||
-rw-r--r-- | doc/tls_cert_summary.html | 66 | ||||
-rw-r--r-- | doc/tls_cert_udp_relay.html | 105 | ||||
-rw-r--r-- | gss-misc.c | 26 | ||||
-rw-r--r-- | omfwd.c | 645 | ||||
-rw-r--r-- | outchannel.c | 2 | ||||
-rw-r--r-- | parse.c | 2 | ||||
-rw-r--r-- | parse.h | 19 | ||||
-rw-r--r-- | plugins/im3195/Makefile.am | 8 | ||||
-rw-r--r-- | plugins/im3195/im3195.c | 167 | ||||
-rw-r--r-- | plugins/imfile/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imfile/imfile.c | 28 | ||||
-rw-r--r-- | plugins/imgssapi/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/imgssapi/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imgssapi/imgssapi.c | 78 | ||||
-rw-r--r-- | plugins/imklog/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/imklog/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imklog/imklog.c | 12 | ||||
-rw-r--r-- | plugins/imklog/imklog.h | 2 | ||||
-rw-r--r-- | plugins/imklog/linux.c | 9 | ||||
-rw-r--r-- | plugins/immark/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/immark/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/immark/immark.c | 5 | ||||
-rw-r--r-- | plugins/imrelp/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/imrelp/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imrelp/imrelp.c | 6 | ||||
-rw-r--r-- | plugins/imtcp/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/imtcp/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imtcp/imtcp.c | 83 | ||||
-rw-r--r-- | plugins/imtemplate/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imudp/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/imudp/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imudp/imudp.c | 17 | ||||
-rw-r--r-- | plugins/imuxsock/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/imuxsock/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/imuxsock/imuxsock.c | 25 | ||||
-rw-r--r-- | plugins/omgssapi/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/omgssapi/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/omgssapi/omgssapi.c | 62 | ||||
-rw-r--r-- | plugins/omlibdbi/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/omlibdbi/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/omlibdbi/omlibdbi.c | 14 | ||||
-rw-r--r-- | plugins/ommail/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/ommail/ommail.c | 12 | ||||
-rw-r--r-- | plugins/ommysql/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/ommysql/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/ommysql/ommysql.c | 10 | ||||
-rw-r--r-- | plugins/ompgsql/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/ompgsql/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/ompgsql/ompgsql.c | 8 | ||||
-rw-r--r-- | plugins/omrelp/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/omrelp/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/omrelp/omrelp.c | 21 | ||||
-rw-r--r-- | plugins/omsnmp/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/omsnmp/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/omsnmp/omsnmp.c | 14 | ||||
-rw-r--r-- | plugins/omtesting/.cvsignore | 6 | ||||
-rw-r--r-- | plugins/omtesting/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/omtesting/omtesting.c | 2 | ||||
-rw-r--r-- | redhat/rsyslog | 12 | ||||
-rw-r--r-- | redhat/rsyslog.conf | 26 | ||||
-rw-r--r-- | redhat/rsyslog.init | 89 | ||||
-rw-r--r-- | redhat/rsyslog.log | 6 | ||||
-rw-r--r-- | redhat/rsyslog.sysconfig | 12 | ||||
-rw-r--r-- | rfc3195d.8 | 84 | ||||
-rw-r--r-- | rfc3195d.c | 289 | ||||
-rw-r--r-- | rsyslog.conf | 7 | ||||
-rw-r--r-- | runtime/Makefile.am | 137 | ||||
-rw-r--r-- | runtime/atomic.h (renamed from atomic.h) | 15 | ||||
-rw-r--r-- | runtime/cfsysline.c (renamed from cfsysline.c) | 36 | ||||
-rw-r--r-- | runtime/cfsysline.h (renamed from cfsysline.h) | 15 | ||||
-rw-r--r-- | runtime/conf.c (renamed from conf.c) | 54 | ||||
-rw-r--r-- | runtime/conf.h (renamed from conf.h) | 0 | ||||
-rw-r--r-- | runtime/ctok.c (renamed from ctok.c) | 16 | ||||
-rw-r--r-- | runtime/ctok.h (renamed from ctok.h) | 0 | ||||
-rw-r--r-- | runtime/ctok_token.c (renamed from ctok_token.c) | 2 | ||||
-rw-r--r-- | runtime/ctok_token.h (renamed from ctok_token.h) | 4 | ||||
-rw-r--r-- | runtime/datetime.c (renamed from datetime.c) | 55 | ||||
-rw-r--r-- | runtime/datetime.h (renamed from datetime.h) | 17 | ||||
-rw-r--r-- | runtime/debug.c (renamed from debug.c) | 21 | ||||
-rw-r--r-- | runtime/debug.h (renamed from debug.h) | 15 | ||||
-rw-r--r-- | runtime/errmsg.c (renamed from errmsg.c) | 60 | ||||
-rw-r--r-- | runtime/errmsg.h (renamed from errmsg.h) | 17 | ||||
-rw-r--r-- | runtime/expr.c (renamed from expr.c) | 2 | ||||
-rw-r--r-- | runtime/expr.h (renamed from expr.h) | 0 | ||||
-rw-r--r-- | runtime/glbl.c | 258 | ||||
-rw-r--r-- | runtime/glbl.h | 62 | ||||
-rw-r--r-- | runtime/linkedlist.c (renamed from linkedlist.c) | 19 | ||||
-rw-r--r-- | runtime/linkedlist.h (renamed from linkedlist.h) | 17 | ||||
-rw-r--r-- | runtime/module-template.h (renamed from module-template.h) | 19 | ||||
-rw-r--r-- | runtime/modules.c (renamed from modules.c) | 41 | ||||
-rw-r--r-- | runtime/modules.h (renamed from modules.h) | 18 | ||||
-rw-r--r-- | runtime/msg.c (renamed from msg.c) | 163 | ||||
-rw-r--r-- | runtime/msg.h (renamed from msg.h) | 21 | ||||
-rw-r--r-- | runtime/net.c (renamed from net.c) | 477 | ||||
-rw-r--r-- | runtime/net.h (renamed from net.h) | 74 | ||||
-rw-r--r-- | runtime/netstrm.c | 353 | ||||
-rw-r--r-- | runtime/netstrm.h | 73 | ||||
-rw-r--r-- | runtime/netstrms.c | 324 | ||||
-rw-r--r-- | runtime/netstrms.h | 64 | ||||
-rw-r--r-- | runtime/nsd.h | 76 | ||||
-rw-r--r-- | runtime/nsd_gtls.c | 1711 | ||||
-rw-r--r-- | runtime/nsd_gtls.h | 92 | ||||
-rw-r--r-- | runtime/nsd_ptcp.c | 781 | ||||
-rw-r--r-- | runtime/nsd_ptcp.h | 47 | ||||
-rw-r--r-- | runtime/nsdsel_gtls.c | 260 | ||||
-rw-r--r-- | runtime/nsdsel_gtls.h | 43 | ||||
-rw-r--r-- | runtime/nsdsel_ptcp.c | 196 | ||||
-rw-r--r-- | runtime/nsdsel_ptcp.h | 44 | ||||
-rw-r--r-- | runtime/nssel.c | 227 | ||||
-rw-r--r-- | runtime/nssel.h | 56 | ||||
-rw-r--r-- | runtime/obj-types.h (renamed from obj-types.h) | 63 | ||||
-rw-r--r-- | runtime/obj.c (renamed from obj.c) | 40 | ||||
-rw-r--r-- | runtime/obj.h (renamed from obj.h) | 15 | ||||
-rw-r--r-- | runtime/objomsr.c (renamed from objomsr.c) | 18 | ||||
-rw-r--r-- | runtime/objomsr.h (renamed from objomsr.h) | 15 | ||||
-rw-r--r-- | runtime/queue.c (renamed from queue.c) | 82 | ||||
-rw-r--r-- | runtime/queue.h (renamed from queue.h) | 0 | ||||
-rw-r--r-- | runtime/regexp.c (renamed from regexp.c) | 0 | ||||
-rw-r--r-- | runtime/regexp.h (renamed from regexp.h) | 0 | ||||
-rw-r--r-- | runtime/rsyslog.c | 237 | ||||
-rw-r--r-- | runtime/rsyslog.h (renamed from rsyslog.h) | 130 | ||||
-rw-r--r-- | runtime/srUtils.h (renamed from srUtils.h) | 16 | ||||
-rw-r--r-- | runtime/srutils.c (renamed from srUtils.c) | 66 | ||||
-rw-r--r-- | runtime/stream.c (renamed from stream.c) | 5 | ||||
-rw-r--r-- | runtime/stream.h (renamed from stream.h) | 0 | ||||
-rw-r--r-- | runtime/stringbuf.c (renamed from stringbuf.c) | 17 | ||||
-rw-r--r-- | runtime/stringbuf.h (renamed from stringbuf.h) | 15 | ||||
-rw-r--r-- | runtime/sync.c (renamed from sync.c) | 0 | ||||
-rw-r--r-- | runtime/sync.h (renamed from sync.h) | 0 | ||||
-rw-r--r-- | runtime/syslogd-types.h (renamed from syslogd-types.h) | 19 | ||||
-rw-r--r-- | runtime/sysvar.c (renamed from sysvar.c) | 0 | ||||
-rw-r--r-- | runtime/sysvar.h (renamed from sysvar.h) | 0 | ||||
-rw-r--r-- | runtime/var.c (renamed from var.c) | 0 | ||||
-rw-r--r-- | runtime/var.h (renamed from var.h) | 0 | ||||
-rw-r--r-- | runtime/vm.c (renamed from vm.c) | 3 | ||||
-rw-r--r-- | runtime/vm.h (renamed from vm.h) | 0 | ||||
-rw-r--r-- | runtime/vmop.c (renamed from vmop.c) | 0 | ||||
-rw-r--r-- | runtime/vmop.h (renamed from vmop.h) | 0 | ||||
-rw-r--r-- | runtime/vmprg.c (renamed from vmprg.c) | 0 | ||||
-rw-r--r-- | runtime/vmprg.h (renamed from vmprg.h) | 0 | ||||
-rw-r--r-- | runtime/vmstk.c (renamed from vmstk.c) | 0 | ||||
-rw-r--r-- | runtime/vmstk.h (renamed from vmstk.h) | 0 | ||||
-rw-r--r-- | runtime/wti.c (renamed from wti.c) | 5 | ||||
-rw-r--r-- | runtime/wti.h (renamed from wti.h) | 0 | ||||
-rw-r--r-- | runtime/wtp.c (renamed from wtp.c) | 33 | ||||
-rw-r--r-- | runtime/wtp.h (renamed from wtp.h) | 0 | ||||
-rw-r--r-- | tcpclt.c | 40 | ||||
-rw-r--r-- | tcpclt.h | 5 | ||||
-rw-r--r-- | tcps_sess.c | 105 | ||||
-rw-r--r-- | tcps_sess.h | 18 | ||||
-rw-r--r-- | tcpsrv.c | 538 | ||||
-rw-r--r-- | tcpsrv.h | 30 | ||||
-rw-r--r-- | tcpsyslog.c | 55 | ||||
-rw-r--r-- | tcpsyslog.h | 38 | ||||
-rw-r--r-- | template.c | 92 | ||||
-rw-r--r-- | template.h | 17 | ||||
-rw-r--r-- | tests/.gitignore | 3 | ||||
-rw-r--r-- | tests/Makefile.am | 14 | ||||
-rw-r--r-- | tests/README | 9 | ||||
-rw-r--r-- | tests/rscript-parse.c | 100 | ||||
-rw-r--r-- | tests/rt-init.c (renamed from liblogging-stub.h) | 70 | ||||
-rw-r--r-- | tests/runtime-dummy.c (renamed from glbl.h) | 35 | ||||
-rw-r--r-- | tests/testbench.h | 102 | ||||
-rw-r--r-- | threads.c | 2 | ||||
-rw-r--r-- | threads.h | 5 | ||||
-rw-r--r-- | tools/Makefile.am | 29 | ||||
-rwxr-xr-x | tools/gnutls/cert-gen-selfsigned | 6 | ||||
-rwxr-xr-x | tools/gnutls/cert-show-fingerprint | 6 | ||||
-rw-r--r-- | tools/iminternal.c (renamed from iminternal.c) | 0 | ||||
-rw-r--r-- | tools/iminternal.h (renamed from iminternal.h) | 0 | ||||
-rw-r--r-- | tools/omdiscard.c (renamed from omdiscard.c) | 0 | ||||
-rw-r--r-- | tools/omdiscard.h (renamed from omdiscard.h) | 0 | ||||
-rw-r--r-- | tools/omfile.c (renamed from omfile.c) | 28 | ||||
-rw-r--r-- | tools/omfile.h (renamed from omfile.h) | 0 | ||||
-rw-r--r-- | tools/omfwd.c | 714 | ||||
-rw-r--r-- | tools/omfwd.h (renamed from omfwd.h) | 0 | ||||
-rw-r--r-- | tools/omshell.c (renamed from omshell.c) | 2 | ||||
-rw-r--r-- | tools/omshell.h (renamed from omshell.h) | 0 | ||||
-rw-r--r-- | tools/omusrmsg.c (renamed from omusrmsg.c) | 188 | ||||
-rw-r--r-- | tools/omusrmsg.h (renamed from omusrmsg.h) | 0 | ||||
-rw-r--r-- | tools/pidfile.c (renamed from pidfile.c) | 0 | ||||
-rw-r--r-- | tools/pidfile.h (renamed from pidfile.h) | 0 | ||||
-rw-r--r-- | tools/rsyslog.conf.5 (renamed from rsyslog.conf.5) | 0 | ||||
-rw-r--r-- | tools/rsyslogd.8 (renamed from rsyslogd.8) | 0 | ||||
-rw-r--r-- | tools/syslogd.c (renamed from syslogd.c) | 343 | ||||
-rw-r--r-- | tools/syslogd.h (renamed from syslogd.h) | 73 |
228 files changed, 10764 insertions, 3375 deletions
@@ -1,6 +1,8 @@ +*~ .tar.gz .deps .libs +*.o *.lo *.la Makefile @@ -18,9 +20,17 @@ ltmain.sh aclocal.m4 depcomp stamp-h1 -rfc3195d -rklogd -rsyslogd INSTALL install-sh missing +compile +rsyslogd +*.orig +rg.conf* +*.swp +# some common names I use during development +utils +tmp* +log +logfile +debug @@ -1,4 +1,167 @@ --------------------------------------------------------------------------- +Version 3.19.12 [BETA] (rgerhards), 2008-08-25 +--------------------------------------------------------------------------- +Version 3.19.11 [BETA] (rgerhards), 2008-08-25 +This is a refresh of the beta. No beta-specific fixes have been added. +- included fixes from v3-stable (most importantly 3.18.3) +--------------------------------------------------------------------------- +Version 3.19.10 [BETA] (rgerhards), 2008-07-15 +- start of a new beta branch based on former 3.19 devel branch +- bugfix: bad memory leak in disk-based queue modes +- bugfix: UDP syslog forwarding did not work on all platforms + the ai_socktype was incorrectly set to 1. On some platforms, this + lead to failing name resolution (e.g. FreeBSD 7). Thanks to HKS for + reporting the bug. +- bugfix: priority was incorrectly calculated on FreeBSD 7, + because the LOG_MAKEPRI() C macro has a different meaning there (it + is just a simple addition of faciltity and severity). I have changed + this to use own, consistent, code for PRI calculation. Thank to HKS + for reporting this bug. +- bugfix (cosmetical): authorization was not checked when gtls handshake + completed immediately. While this sounds scary, the situation can not + happen in practice. We use non-blocking IO only for server-based gtls + session setup. As TLS requires the exchange of multiple frames before + the handshake completes, it simply is impossible to do this in one + step. However, it is useful to have the code path correct even for + this case - otherwise, we may run into problems if the code is changed + some time later (e.g. to use blocking sockets). Thanks to varmojfekoj + for providing the patch. +- important queue bugfix from 3.18.1 imported (see below) +- cleanup of some debug messages +--------------------------------------------------------------------------- +Version 3.19.9 (rgerhards), 2008-07-07 +- added tutorial for creating a TLS-secured syslog infrastructure +- rewritten omusrmsg to no longer fork() a new process for sending messages + this caused some problems with the threading model, e.g. zombies. Also, + it was far less optimal than it is now. +- bugfix: machine certificate was required for client even in TLS anon mode + Reference: http://bugzilla.adiscon.com/show_bug.cgi?id=85 + The fix also slightly improves performance by not storing certificates in + client sessions when there is no need to do so. +- bugfix: RainerScript syntax error was not always detected +--------------------------------------------------------------------------- +Version 3.19.8 (rgerhards), 2008-07-01 +- bugfix: gtls module did not correctly handle EGAIN (and similar) recv() + states. This has been fixed by introducing a new abstraction layer inside + gtls. +- added (internal) error codes to error messages; added redirector to + web description of error codes + closes bug http://bugzilla.adiscon.com/show_bug.cgi?id=20 +- disabled compile warnings caused by third-party libraries +- reduced number of compile warnings in gcc's -pedantic mode +- some minor documentation improvements +- included all fixes from beta 3.17.5 +--------------------------------------------------------------------------- +Version 3.19.7 (rgerhards), 2008-06-11 +- added new property replacer option "date-subseconds" that enables + to query just the subsecond part of a high-precision timestamp +- somewhat improved plain tcp syslog reliability by doing a connection + check before sending. Credits to Martin Schuette for providing the + idea. Details are available at + http://blog.gerhards.net/2008/06/reliable-plain-tcp-syslog-once-again.html +- made rsyslog tickless in the (usual and default) case that repeated + message reduction is turned off. More info: + http://blog.gerhards.net/2008/06/coding-to-save-environment.html +- some build system cleanup, thanks to Michael Biebl +- bugfix: compile under (Free)BSD failed due to some invalid library + definitions - this is fixed now. Thanks to Michael Biebl for the patch. +--------------------------------------------------------------------------- +Version 3.19.6 (rgerhards), 2008-06-06 +- enhanced property replacer to support multiple regex matches +- bugfix: part of permittedPeer structure was not correctly initialized + thanks to varmojfekoj for spotting this +- bugfix: off-by-one bug during certificate check +- bugfix: removed some memory leaks in TLS code +--------------------------------------------------------------------------- +Version 3.19.5 (rgerhards), 2008-05-30 +- enabled Posix ERE expressions inside the property replacer + (previously BRE was permitted only) +- provided ability to specify that a regular expression submatch shall + be used inside the property replacer +- implemented in property replacer: if a regular expression does not match, + it can now either return "**NO MATCH** (default, as before), a blank + property or the full original property text +- enhanced property replacer to support multiple regex matches +--------------------------------------------------------------------------- +Version 3.19.4 (rgerhards), 2008-05-27 +- implemented x509/certvalid gtls auth mode +- implemented x509/name gtls auth mode (including wildcards) +- changed fingerprint gtls auth mode to new format fingerprint +- protected gtls error string function by a mutex. Without it, we + could have a race condition in extreme cases. This was very remote, + but now can no longer happen. +- changed config directive name to reflect different use + $ActionSendStreamDriverCertFingerprint is now + $ActionSendStreamDriverPermittedPeer and can be used both for + fingerprint and name authentication (similar to the input side) +- bugfix: sender information (fromhost et al) was missing in imudp + thanks to sandiso for reporting this bug +- this release fully inplements IETF's syslog-transport-tls-12 plus + the latest text changes Joe Salowey provided via email. Not included + is ipAddress subjectAltName authentication, which I think will be + dropped from the draft. I don't think there is any real need for it. +This release also includes all bug fix up to today from the beta +and stable branches. Most importantly, this means the bugfix for +100% CPU utilization by imklog. +--------------------------------------------------------------------------- +Version 3.19.3 (rgerhards), 2008-05-21 +- added ability to authenticate the server against its certificate + fingerprint +- added ability for client to provide its fingerprint +- added ability for server to obtain client cert's fingerprint +- bugfix: small mem leak in omfwd on exit (strmdriver name was not freed) +- bugfix: $ActionSendStreamDriver had no effect +- bugfix: default syslog port was no longer used if none was + configured. Thanks to varmojfekoj for the patch +- bugfix: missing linker options caused build to fail on some + systems. Thanks to Tiziano Mueller for the patch. +--------------------------------------------------------------------------- +Version 3.19.2 (rgerhards), 2008-05-16 +- bugfix: TCP input modules did incorrectly set fromhost property + (always blank) +- bugfix: imklog did not set fromhost property +- added "fromhost-ip" property + Note that adding this property changes the on-disk format for messages. + However, that should not have any bad effect on existing spool files. + But you will run into trouble if you create a spool file with this + version and then try to process it with an older one (after a downgrade). + Don't do that ;) +- added "RSYSLOG_DebugFormat" canned template +- bugfix: hostname and fromhost were swapped when a persisted message + (in queued mode) was read in +- bugfix: lmtcpclt, lmtcpsrv and lmgssutil did all link to the static + runtime library, resulting in a large size increase (and potential + "interesting" effects). Thanks to Michael Biebel for reporting the size + issue. +- bugfix: TLS server went into an endless loop in some situations. + Thanks to Michael Biebl for reporting the problem. +- fixed potential segfault due to invalid call to cfsysline + thanks to varmojfekoj for the patch +--------------------------------------------------------------------------- +Version 3.19.1 (rgerhards), 2008-05-07 +- configure help for --enable-gnutls wrong - said default is "yes" but + default actually is "no" - thanks to darix for pointing this out +- file dirty.h was missing - thanks to darix for pointing this out +- bugfix: man files were not properly distributed - thanks to + darix for reporting and to Michael Biebl for help with the fix +- some minor cleanup +--------------------------------------------------------------------------- +Version 3.19.0 (rgerhards), 2008-05-06 +- begins new devel branch version +- implemented TLS for plain tcp syslog (this is also the world's first + implementation of IETF's upcoming syslog-transport-tls draft) +- partly rewritten and improved omfwd among others, now loads TCP + code only if this is actually necessary +- split of a "runtime library" for rsyslog - this is not yet a clean + model, because some modularization is still outstanding. In theory, + this shall enable other utilities but rsyslogd to use the same + runtime +- implemented im3195, the RFC3195 input as a plugin +- changed directory structure, files are now better organized +- a lot of cleanup in regard to modularization +- -c option no longer must be the first option - thanks to varmjofekoj + for the patch +--------------------------------------------------------------------------- Version 3.18.5 (rgerhards), 2008-10-?? - added doc on malformed messages, cause and how to work-around, to the doc set diff --git a/Makefile.am b/Makefile.am index 0e75710c..e78a413c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,125 +1,9 @@ -#sbin_PROGRAMS = rfc3195d rsyslogd sbin_PROGRAMS = -man_MANS = - -if ENABLE_RSYSLOGD -sbin_PROGRAMS += rsyslogd -rsyslogd_SOURCES = \ - datetime.c \ - datetime.h \ - errmsg.c \ - errmsg.h \ - syslogd.c \ - syslogd.h \ - sysvar.c \ - sysvar.h \ - vm.c \ - vm.h \ - vmstk.c \ - vmstk.h \ - vmprg.c \ - vmprg.h \ - vmop.c \ - vmop.h \ - debug.c \ - debug.h \ - glbl.h \ - pidfile.c \ - pidfile.h \ - template.c \ - outchannel.c \ - stringbuf.c \ - stringbuf.h \ - srUtils.c \ - srUtils.h \ - parse.c \ - parse.h \ - syslogd-types.h \ - template.h \ - outchannel.h \ - liblogging-stub.h \ - threads.c \ - threads.h \ - stream.c \ - stream.h \ - var.c \ - var.h \ - wtp.c \ - wtp.h \ - wti.c \ - wti.h \ - queue.c \ - queue.h \ - sync.c \ - sync.h \ - obj.c \ - obj.h \ - obj-types.h \ - msg.c \ - msg.h \ - expr.c \ - expr.h \ - ctok.c \ - ctok.h \ - ctok_token.c \ - ctok_token.h \ - conf.c \ - conf.h \ - omshell.c \ - omshell.h \ - omusrmsg.c \ - omusrmsg.h \ - omfwd.c \ - omfwd.h \ - tcpsyslog.c \ - tcpsyslog.h \ - omfile.c \ - omfile.h \ - omdiscard.c \ - omdiscard.h \ - modules.c \ - modules.h \ - module-template.h \ - objomsr.c \ - objomsr.h \ - cfsysline.c \ - cfsysline.h \ - linkedlist.c \ - linkedlist.h \ - iminternal.c \ - iminternal.h \ - action.c \ - action.h \ - atomic.h - -rsyslogd_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/\" $(pthreads_cflags) -rsyslogd_LDADD = $(zlib_libs) $(pthreads_libs) $(dl_libs) $(rt_libs) -rsyslogd_LDFLAGS = -export-dynamic - -man_MANS += rsyslogd.8 rsyslog.conf.5 - -endif # if ENABLE_RSYSLOGD - -# now come the library plugins pkglib_LTLIBRARIES = -if ENABLE_RFC3195 -# this does so far not work - a manual build is needed -sbin_PROGRAMS += rfc3195d -rfc3195d_SOURCES = rfc3195d.c rsyslog.h -man_MANS += rfc3195d.8 -endif - if ENABLE_INET -pkglib_LTLIBRARIES += lmnet.la lmtcpsrv.la lmtcpclt.la -# -# network support -# -lmnet_la_SOURCES = net.c net.h -lmnet_la_CPPFLAGS = $(pthreads_cflags) -lmnet_la_LDFLAGS = -module -avoid-version -lmnet_la_LIBADD = +pkglib_LTLIBRARIES += lmtcpsrv.la lmtcpclt.la # # # TCP (stream) server support @@ -129,7 +13,7 @@ lmtcpsrv_la_SOURCES = \ tcps_sess.h \ tcpsrv.c \ tcpsrv.h -lmtcpsrv_la_CPPFLAGS = $(pthreads_cflags) +lmtcpsrv_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmtcpsrv_la_LDFLAGS = -module -avoid-version lmtcpsrv_la_LIBADD = @@ -139,50 +23,41 @@ lmtcpsrv_la_LIBADD = lmtcpclt_la_SOURCES = \ tcpclt.c \ tcpclt.h -lmtcpclt_la_CPPFLAGS = $(pthreads_cflags) +lmtcpclt_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmtcpclt_la_LDFLAGS = -module -avoid-version lmtcpclt_la_LIBADD = endif # if ENABLE_INET # -# regular expression support -# -if ENABLE_REGEXP -pkglib_LTLIBRARIES += lmregexp.la -lmregexp_la_SOURCES = regexp.c regexp.h -lmregexp_la_CPPFLAGS = $(pthreads_cflags) -lmregexp_la_LDFLAGS = -module -avoid-version -lmregexp_la_LIBADD = -endif - -# # gssapi support # if ENABLE_GSSAPI pkglib_LTLIBRARIES += lmgssutil.la lmgssutil_la_SOURCES = gss-misc.c gss-misc.h -lmgssutil_la_CPPFLAGS = $(pthreads_cflags) +lmgssutil_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmgssutil_la_LDFLAGS = -module -avoid-version lmgssutil_la_LIBADD = $(gss_libs) endif EXTRA_DIST = \ - redhat/rsyslog.conf \ - redhat/rsyslog.init \ - redhat/rsyslog.log \ - redhat/rsyslog.sysconfig \ freebsd/rsyslogd \ slackware/rc.rsyslogd \ contrib/README \ rsyslog.conf \ COPYING.LESSER \ - $(man_MANS) + contrib/gnutls/ca.pem \ + contrib/gnutls/cert.pem \ + contrib/gnutls/key.pem -SUBDIRS = . doc +SUBDIRS = doc runtime . tests SUBDIRS += plugins/immark plugins/imuxsock plugins/imtcp plugins/imudp plugins/omtesting +if ENABLE_RSYSLOGD +SUBDIRS += tools +endif + if ENABLE_IMKLOG SUBDIRS += plugins/imklog endif @@ -222,3 +97,7 @@ endif if ENABLE_MAIL SUBDIRS += plugins/ommail endif + +if ENABLE_RFC3195 +SUBDIRS += plugins/im3195 +endif @@ -34,7 +34,7 @@ #include <time.h> #include <errno.h> -#include "syslogd.h" +#include "dirty.h" #include "template.h" #include "action.h" #include "modules.h" @@ -219,11 +219,11 @@ actionConstructFinalize(action_t *pThis) /* ... set some properties ... */ # define setQPROP(func, directive, data) \ CHKiRet_Hdlr(func(pThis->pQueue, data)) { \ - errmsg.LogError(NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ + errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ } # define setQPROPstr(func, directive, data) \ CHKiRet_Hdlr(func(pThis->pQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \ - errmsg.LogError(NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ + errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ } queueSetpUsr(pThis->pQueue, pThis); @@ -369,6 +369,7 @@ rsRetVal actionDbgPrint(action_t *pThis) /* call the DoAction output plugin entry point * rgerhards, 2008-01-28 */ +#pragma GCC diagnostic ignored "-Wempty-body" rsRetVal actionCallDoAction(action_t *pAction, msg_t *pMsg) { @@ -453,6 +454,7 @@ finalize_it: RETiRet; } +#pragma GCC diagnostic warning "-Wempty-body" /* set the action message queue mode * TODO: probably move this into queue object, merge with MainMsgQueue! @@ -475,7 +477,7 @@ static rsRetVal setActionQueType(void __attribute__((unused)) *pVal, uchar *pszT ActionQueType = QUEUETYPE_DIRECT; dbgprintf("action queue type set to DIRECT (no queueing at all)\n"); } else { - errmsg.LogError(NO_ERRCODE, "unknown actionqueue parameter: %s", (char *) pszType); + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown actionqueue parameter: %s", (char *) pszType); iRet = RS_RET_INVALID_PARAMS; } d_free(pszType); /* no longer needed */ @@ -541,7 +543,7 @@ actionWriteToAction(action_t *pAction) pAction->f_pMsg = pMsg; /* use the new msg (pointer will be restored below) */ } - dbgprintf("Called action, logging to %s", module.GetStateName(pAction->pMod)); + dbgprintf("Called action, logging to %s\n", module.GetStateName(pAction->pMod)); time(&now); /* we need this for message repeation processing AND $ActionExecOnlyOnceEveryInterval */ if(pAction->tLastExec > now) { @@ -592,6 +594,7 @@ finalize_it: /* call the configured action. Does all necessary housekeeping. * rgerhards, 2007-08-01 */ +#pragma GCC diagnostic ignored "-Wempty-body" rsRetVal actionCallAction(action_t *pAction, msg_t *pMsg) { @@ -676,6 +679,7 @@ finalize_it: pthread_setcancelstate(iCancelStateSave, NULL); RETiRet; } +#pragma GCC diagnostic warning "-Wempty-body" /* add our cfsysline handlers @@ -763,14 +767,14 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques " Could not find template '%s' - action disabled\n", pTplName); errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_NOT_FOUND, "%s", errMsg); ABORT_FINALIZE(RS_RET_NOT_FOUND); } /* check required template options */ if( (iTplOpts & OMSR_RQD_TPL_OPT_SQL) && (pAction->ppTpl[i]->optFormatForSQL == 0)) { errno = 0; - errmsg.LogError(NO_ERRCODE, "Action disabled. To use this action, you have to specify " + errmsg.LogError(0, RS_RET_RQD_TPLOPT_MISSING, "Action disabled. To use this action, you have to specify " "the SQL or stdSQL option in your template!\n"); ABORT_FINALIZE(RS_RET_RQD_TPLOPT_MISSING); } diff --git a/configure.ac b/configure.ac index 892a2ab9..d70e48b5 100644 --- a/configure.ac +++ b/configure.ac @@ -2,9 +2,9 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[3.18.5],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[3.19.12],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE -AC_CONFIG_SRCDIR([syslogd.c]) +AC_CONFIG_SRCDIR([ChangeLog]) AC_CONFIG_HEADERS([config.h]) AC_GNU_SOURCE @@ -467,6 +467,63 @@ AC_SUBST(snmp_cflags) AC_SUBST(snmp_libs) +# GNUtls support +AC_ARG_ENABLE(gnutls, + [AS_HELP_STRING([--enable-gnutls],[Enable GNU TLS support @<:@default=no@:>@])], + [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@:>@])], + [case "${enableval}" in + yes) enable_rsyslogrt="yes" ;; + no) enable_rsyslogrt="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-rsyslogrt) ;; + esac], + [enable_rsyslogrt=yes] +) +if test "x$enable_rsyslogrt" = "xyes"; then + rsrt_cflags="-I\$(top_srcdir)/runtime -I\$(top_srcdir)" + rsrt_libs="\$(top_builddir)/runtime/librsyslog.la" +fi +AM_CONDITIONAL(ENABLE_RSYSLOGRT, test x$enable_rsyslogrt = xyes) +AC_SUBST(rsrt_cflags) +AC_SUBST(rsrt_libs) + + # support for NOT building rsyslogd (useful for source-based packaging systems) AC_ARG_ENABLE(rsyslogd, [AS_HELP_STRING([--enable-rsyslogd],[Build rsyslogd @<:@default=yes@:>@])], @@ -511,7 +568,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 @@ -522,25 +578,11 @@ AC_ARG_ENABLE(rfc3195, [enable_rfc3195=no] ) if test "x$enable_rfc3195" = "xyes"; then - AC_CHECK_HEADERS( - [librfc3195.h],, - [AC_MSG_FAILURE([RFC3195 library is missing (no headers)])] - ) -# I don't know how to tell that librfc3195 needs -lrt, so I disable -# this check for now - the header check should work well enough... -# rgerhards, 2008-03-25 -# AC_CHECK_LIB( -# [rfc3195], -# [rfc3195EngineGetVersion], -# [rfc3195_cflags="" - rfc3195_libs="-lrfc3195" -# ], -# [AC_MSG_FAILURE([RFC3195 library is missing])] -# ) + PKG_CHECK_MODULES(LIBLOGGING, liblogging >= 0.7.1) fi AM_CONDITIONAL(ENABLE_RFC3195, test x$enable_rfc3195 = xyes) -AC_SUBST(rfc3195_cflags) -AC_SUBST(rfc3195_libs) +AC_SUBST(RFC3195_CFLAGS) +AC_SUBST(RFC3195_LIBS) # settings for the template input module; copy and modify this code @@ -584,9 +626,13 @@ AM_CONDITIONAL(ENABLE_IMTEMPLATE, test x$enable_imtemplate = xyes) AC_CONFIG_FILES([Makefile \ + runtime/Makefile \ + tools/Makefile \ + tests/Makefile \ doc/Makefile \ plugins/imudp/Makefile \ plugins/imtcp/Makefile \ + plugins/im3195/Makefile \ plugins/imgssapi/Makefile \ plugins/imuxsock/Makefile \ plugins/immark/Makefile \ @@ -621,9 +667,11 @@ 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 "valgrind support settings enabled: $enable_valgrind" +echo "rsyslog runtime will be built: $enable_rsyslogrt" echo "rsyslogd will be built: $enable_rsyslogd" diff --git a/contrib/gnutls/ca-key.pem b/contrib/gnutls/ca-key.pem new file mode 100644 index 00000000..181a8ad9 --- /dev/null +++ b/contrib/gnutls/ca-key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDDaz5X5YIruPH0wukMPik7xIKqrpCcr8Gm28oz5h4GtX253eWr +piBuk2a/f/CKDjeuqmiWqTs90PFNb+Z1c+Yzvagqv80VzZwDI4RcrwlNaKrBz/9X +iowCcoV8s7GvV2vtZEPSThNzz4FYkxCMvbOYZeJIYQVhZggUcuadfhmDIwIDAQAB +AoGAIG5AUD2jmYDzD+UhiultVgtkifyNaEtsuQsZu/zbt85P2VQ0z4SINlbvrXvc +iJ9tEzzEPa3udHGj/MTDe3OAB4TK5tImX1pe2gw+zaOB/DaH5i4QhXeltU7epCHF +oUv9EVNzL8Bl00MFiWcLY0LisQVfHeW5rcN9U7EbvTlWbRkCQQDR2/Qn1ceavwDU +qYt2TbEicJVC8aQMYYyc6Xvi4mZaNa8gGCpWpurgQop0Ln0QE8vA0601UVs6N3tm +g8FJ8rXpAkEA7mKCtp2MXCbHMdkZCyQt6drUYCyU9N/HtmBEtFGpqt1PdMyUI07m +rlVFDwUH9JFmg18RP1X2ufj7+ZbJzaMtKwJBAJgbw1Z0P19Mfj+mPC2dlnyN+cIx +/2Px+Mdq/J6w1tsf+jVbDqUMC0ZNNKmNYJycnJzBUNRKicMin9DoQttkjrECQQCC +s/aRY+6adBSRi0QE7NBTwUzicm81mCDrKPtilsfdTDyNgMHUXiVy/oO/yXVkLfi0 +HQLa5CpEK3UUkw2Qt2BDAkA0XXvQzW0+tEHiktLNljIluhiyOAx2bBywY/9Qmn6C +hv4sOSCzTR39jNmuNZ0X6ZZvt4VsWTHhpche/ud1+3p6 +-----END RSA PRIVATE KEY----- diff --git a/contrib/gnutls/ca.pem b/contrib/gnutls/ca.pem new file mode 100644 index 00000000..6324c7d5 --- /dev/null +++ b/contrib/gnutls/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICYjCCAc2gAwIBAgIBATALBgkqhkiG9w0BAQUwWDELMAkGA1UEBhMCREUxHTAb +BgNVBAoTFHJzeXNsb2cgdGVzdCByb290IENBMQswCQYDVQQLEwJDQTEdMBsGA1UE +AxMUcnN5c2xvZy10ZXN0LXJvb3QtY2EwHhcNMDgwNTIwMTI1ODEyWhcNMTgwNTE4 +MTI1ODI0WjBYMQswCQYDVQQGEwJERTEdMBsGA1UEChMUcnN5c2xvZyB0ZXN0IHJv +b3QgQ0ExCzAJBgNVBAsTAkNBMR0wGwYDVQQDExRyc3lzbG9nLXRlc3Qtcm9vdC1j +YTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGAw2s+V+WCK7jx9MLpDD4pO8SCqq6Q +nK/BptvKM+YeBrV9ud3lq6YgbpNmv3/wig43rqpolqk7PdDxTW/mdXPmM72oKr/N +Fc2cAyOEXK8JTWiqwc//V4qMAnKFfLOxr1dr7WRD0k4Tc8+BWJMQjL2zmGXiSGEF +YWYIFHLmnX4ZgyMCAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8E +BQMDBwYAMB0GA1UdDgQWBBQzYQQgUm0YLNdarJnc2c1LxYVClDALBgkqhkiG9w0B +AQUDgYEAuGWtH7Jkpa0n/izqQ5ddDQP/LT6taivCwlpEYEU9aumpQPWWxtYywKaP +RfM1JTMLAiYd8MS7TJ8TYRvvR32Y02Y+OhXn11xERkWvBT2M9yzqX6hDfRueN7RT +fPWsfm/NBTVojzjaECcTFenZid7PC5JiFbcU6PSUMZ49/JPhxAo= +-----END CERTIFICATE----- diff --git a/contrib/gnutls/cert.pem b/contrib/gnutls/cert.pem new file mode 100644 index 00000000..6b5b13cd --- /dev/null +++ b/contrib/gnutls/cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIChjCCAfGgAwIBAgIBADALBgkqhkiG9w0BAQUwWDELMAkGA1UEBhMCREUxHTAb +BgNVBAoTFHJzeXNsb2cgdGVzdCByb290IENBMQswCQYDVQQLEwJDQTEdMBsGA1UE +AxMUcnN5c2xvZy10ZXN0LXJvb3QtY2EwHhcNMDgwNTIwMTMwNDE5WhcNMTgwNTE4 +MTMwNDI2WjA6MQswCQYDVQQGEwJERTEQMA4GA1UEChMHcnN5c2xvZzEZMBcGA1UE +CxMQdGVzdCBjZXJ0aWZpY2F0ZTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGAxmHe +fztJgaGxFYEceiUg0hdMlRVWBqoZelJ8BeXTDnXcu/5F2HtM+l+QDyDaGjKlx+NI +K4rkj7d6Wd3AKPgOYS0VSDZe3a1xf9rRYzOthWTv7tYi4/LTqPXqN5lKE71dgrB/ +/gOmvV/1YD776FIxVGCSAT0hHwkFC3slmpJSwD8CAwEAAaOBhDCBgTAMBgNVHRMB +Af8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATASBgNVHREECzAJ +ggdyc3lzbG9nMB0GA1UdDgQWBBQYu6eC9UALvC+5K5VOnFRi5OC98TAfBgNVHSME +GDAWgBQzYQQgUm0YLNdarJnc2c1LxYVClDALBgkqhkiG9w0BAQUDgYEAXaymqsG9 +PNBhhWIRFvXCDMaDM71vUtgSFoNUbxIV607ua2HQosPPM4EHIda6N6hdBK1bMQoG +yqBwhvw0JVaVaO70Kbs2m2Ypk3YcpJtRqyp8q8+2y/w1Mk1QazFZC29aYgX2iNVf +X4/x38YEL7Gu5vqPrTn++agnV4ZXECKuvLQ= +-----END CERTIFICATE----- diff --git a/contrib/gnutls/key.pem b/contrib/gnutls/key.pem new file mode 100644 index 00000000..3ff507f0 --- /dev/null +++ b/contrib/gnutls/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDGYd5/O0mBobEVgRx6JSDSF0yVFVYGqhl6UnwF5dMOddy7/kXY +e0z6X5APINoaMqXH40griuSPt3pZ3cAo+A5hLRVINl7drXF/2tFjM62FZO/u1iLj +8tOo9eo3mUoTvV2CsH/+A6a9X/VgPvvoUjFUYJIBPSEfCQULeyWaklLAPwIDAQAB +AoGARIwKqmHc+0rYenq7UUVE+vMMBjNyHyllVkvsCMmpzMRS+i5ZCf1I0vZ0O5X5 +ZrX7bH8PL+R1J2eZgjXKMR3NMZBuyKHewItD9t2rIC0eD/ITlwq3VybbaMsw666e +INxSmax+dS5CEcLevHHP3c+Q7S7QAFiWV43TdFUGXWJktIkCQQDPQ5WAZ+/Tvv0Q +vtRjXMeTVaw/bSuKNUeDzFkmGyePnFeCReNFtJLE9PFSQWcPuYcbZgU59JTfA5ac +Un+cHm31AkEA9Qek+q7PcJ+kON9E6SNodCZn6gLyHjnWrq4tf8pZO3NvoX2QiuD4 +rwF7KWjr6q1JzADpLtwXnuYEhyiLFjJA4wJAcElMCEnG2y+ASH8p7z7HfKGQdLg/ +O1wMB3JA5e0WLK5lllUogI4IaZ3N02NNY25+rLBDqpc/w+ZcxQnIypqNtQJATs9p +ofON5wSB1oUBbhckZo9fxuWxqEUkJsUA/2Q+9R843XE8h166vdc1HOmRT8bywHne +hmLl+gazmCFTMw1wzwJAHng+3zGUl4D8Ov3MPFD6hwYYK6/pEdtz/NUsCSazF7eK +XuuP+DXPHNhXOuF1A3tP74pfc/fC1uCUH2G5z3Fy0Q== +-----END RSA PRIVATE KEY----- diff --git a/dirty.h b/dirty.h new file mode 100644 index 00000000..2cf00b40 --- /dev/null +++ b/dirty.h @@ -0,0 +1,81 @@ +/* This file is an aid to support non-modular object accesses + * while we do not have fully modularized everything. Once this is + * done, this file can (and should) be deleted. Presence of it + * also somewhat indicates that the runtime library is not really + * yet a runtime library, because it depends on some functionality + * residing somewhere else. + * + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#ifndef DIRTY_H_INCLUDED +#define DIRTY_H_INCLUDED 1 + +#define MAXLINE 2048 /* maximum line length */ + +/* Flags to logmsg(). + */ +#define NOFLAG 0x000 /* no flag is set (to be used when a flag must be specified and none is required) */ +#define INTERNAL_MSG 0x001 /* msg generated by logmsgInternal() --> special handling */ +/* NO LONGER USED: #define SYNC_FILE 0x002 / * do fsync on file after printing */ +#define ADDDATE 0x004 /* add a date to the message */ +#define MARK 0x008 /* this message is a mark */ + +#define MSG_PARSE_HOSTNAME 1 +#define MSG_DONT_PARSE_HOSTNAME 0 + +rsRetVal submitMsg(msg_t *pMsg); +rsRetVal logmsgInternal(int iErr, int pri, uchar *msg, int flags); +rsRetVal parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType); + +/* TODO: the following 2 need to go in conf obj interface... */ +rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName); +rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl); + +/* Intervals at which we flush out "message repeated" messages, + * in seconds after previous message is logged. After each flush, + * we move to the next interval until we reach the largest. + * TODO: move this to action object! Only action.c and syslogd.c use it. + */ +extern int bActExecWhenPrevSusp; +extern int iActExecOnceInterval; +extern int MarkInterval; +extern int repeatinterval[2]; +extern int bReduceRepeatMsgs; +#define MAXREPEAT ((int)((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)) +#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) +#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \ + (f)->f_repeatcount = MAXREPEAT; \ + } +#ifdef USE_NETZIP +/* config param: minimum message size to try compression. The smaller + * the message, the less likely is any compression gain. We check for + * gain before we submit the message. But to do so we still need to + * do the (costly) compress() call. The following setting sets a size + * for which no call to compress() is done at all. This may result in + * a few more bytes being transmited but better overall performance. + * Note: I have not yet checked the minimum UDP packet size. It might be + * that we do not save anything by compressing very small messages, because + * UDP might need to pad ;) + * rgerhards, 2006-11-30 + */ +#define MIN_SIZE_FOR_COMPRESS 60 +#endif + +#endif /* #ifndef DIRTY_H_INCLUDED */ diff --git a/doc/.cvsignore b/doc/.cvsignore deleted file mode 100644 index 282522db..00000000 --- a/doc/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -Makefile -Makefile.in diff --git a/doc/Makefile.am b/doc/Makefile.am index 518b90ed..de3675de 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -21,6 +21,7 @@ html_files = \ rsyslog_high_database_rate.html \ rsyslog_php_syslog_ng.html \ rsyslog_recording_pri.html \ + rsyslog_tls.html \ rsyslog_reliable_forwarding.html \ rsyslog_stunnel.html \ syslog-protocol.html \ @@ -37,9 +38,21 @@ html_files = \ imklog.html \ professional_support.html \ queues.html \ - queueWorkerLogic.dia \ + src/queueWorkerLogic.dia \ queueWorkerLogic.jpg \ queueWorkerLogic_small.jpg \ + tls_cert_100.jpg \ + tls_cert_ca.jpg \ + tls_cert.jpg \ + tls_cert_errmsgs.html \ + rsyslog_secure_tls.html \ + tls_cert_server.html \ + tls_cert_ca.html \ + tls_cert_summary.html \ + tls_cert_machine.html \ + tls_cert_udp_relay.html \ + tls_cert_client.html \ + tls_cert_scenario.html \ rainerscript.html \ rscript_abnf.html \ rsconf1_actionexeconlywhenpreviousissuspended.html \ @@ -72,6 +85,11 @@ html_files = \ rsconf1_resetconfigvariables.html \ rsconf1_umask.html \ v3compatibility.html \ + im3195.html \ + netstream.html \ + ns_gtls.html \ + ns_ptcp.html \ + src/tls_cert.dia \ gssapi.html \ licensing.html \ ommail.html \ diff --git a/doc/features.html b/doc/features.html index 13fc34c6..2b3b31d9 100644 --- a/doc/features.html +++ b/doc/features.html @@ -1,5 +1,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head><title>rsyslog features</title></head> +<html><head><title>rsyslog features</title> + +</head> <body> <h1>RSyslog - Features</h1> <p><b>This page lists both current features as well as @@ -23,13 +25,18 @@ to MySQL databases</a></li> <li> native support for writing to Postgres databases</li> <li>direct support for Firebird/Interbase, OpenTDS (MS SQL, Sybase), SQLLite, Ingres, Oracle, and mSQL via libdbi, -a database abstraction layer (almost as good as native)</li><li>native support for <a href="ommail.html">sending mail messages</a> (first seen in 3.17.0)</li> +a database abstraction layer (almost as good as native)</li> +<li>native support for <a href="ommail.html">sending +mail messages</a> (first seen in 3.17.0)</li> <li>support for (plain) tcp based syslog - much better reliability</li> <li>support for sending and receiving compressed syslog messages</li> <li>support for on-demand on-disk spooling of messages that can not be processed fast enough (a great feature for <a href="rsyslog_high_database_rate.html">writing massive -amounts of syslog messages to a database</a>)</li><li>support for selectively <a href="http://wiki.rsyslog.com/index.php/OffPeakHours">processing messages only during specific timeframes</a> and spooling them to disk otherwise</li> +amounts of syslog messages to a database</a>)</li> +<li>support for selectively <a href="http://wiki.rsyslog.com/index.php/OffPeakHours">processing +messages only during specific timeframes</a> and spooling them to +disk otherwise</li> <li>ability to monitor text files and convert their contents into syslog messages (one per line)</li> <li>ability to configure backup syslog/database servers - if @@ -49,8 +56,9 @@ substrings</li> command execution</li> <li>support for running multiple rsyslogd instances on a single machine</li> -<li>support for <a href="rsyslog_stunnel.html"> -ssl-protected syslog</a> (via stunnel)</li> +<li>support for <a href="rsyslog_tls.html">TLS-protected +syslog</a> (both <a href="rsyslog_tls.html">natively</a> +and via <a href="rsyslog_stunnel.html">stunnel</a>)</li> <li>ability to filter on any part of the message, not just facility and severity</li> <li>ability to use regular expressions in filters</li> @@ -70,8 +78,7 @@ high log volume on multicore machines)</li> compliant messages (it is volatile because standardization is currently underway and this is a proof-of-concept implementation to aid this effort)</li> -<li> experimental support for syslog-transport-tls based -framing on syslog/tcp connections</li> +<li> world's first implementation of syslog-transport-tls</li> <li> the sysklogd's klogd functionality is implemented as the <i>imklog</i> input plug-in. So rsyslog is a full replacement for the sysklogd package</li> <li> support for IPv6</li> @@ -90,7 +97,13 @@ via custom plugins</li> <li>support for arbitrary complex boolean, string and arithmetic expressions in message filters</li> </ul> -<p> </p> +<h2>World's first</h2> +Rsyslog has an interesting number of "world's firsts" - things that +were implemented for the first time ever in rsyslog. Some of them are still features not available elsewhere.<br><ul> +<li>world's first implementation of IETF I-D syslog-protocol (February 2006, version 1.12.2 and above)</li><li>world's first implementation of dynamic syslog on-the-wire compression (December 2006, version 1.13.0 and above)</li><li>world's first open-source implementation of a disk-queueing syslogd (January 2008, version 3.11.0 and above)</li> +<li>world's first implementation of IETF I-D +syslog-transport-tls (May 2008, version 3.19.0 and above)</li> +</ul> <h2>Upcoming Features</h2> <p>The list below is something like a repository of ideas we'd like to implement. Features on this list are typically NOT scheduled @@ -101,17 +114,13 @@ typically within reach of implementation. Users are encouraged to submit feature requests there (or via our forums). If we like them but they look quite long-lived (aka "not soon to be implemented"), they will possibly be migrated to this list here and at some time moved back -to the sourceforge tracker.</p> +to the bugzilla tracker.</p> <ul> -<li>implement native email-functionality in selector (probably -best done as a plug-in)</li> <li>port it to more *nix variants (eg AIX and HP UX) - this needs volunteers with access to those machines and knowledge </li> -<li>support for native SSL enryption of plain tcp syslog -sessions. This will most probably happen based on syslog-transport-tls.</li> <li>pcre filtering - maybe (depending on feedback) - simple regex already partly added. So far, this seems sufficient so -that there is no urgent need to do pcre</li> +that there is no urgent need to do pcre. If done, it will be a loadable RainerScript function.</li> <li>support for <a href="http://www.monitorware.com/Common/en/glossary/rfc3195.php">RFC 3195</a> as a sender - this is currently unlikely to happen, because there is no real demand for it. Any work on RFC 3195 has been @@ -124,4 +133,4 @@ future of RFC 3195 in rsyslog</a>.</li> <p>To see when each feature was added, see the <a href="http://www.rsyslog.com/Topic4.phtml">rsyslog change log</a> (online only).</p> -</body></html>
\ No newline at end of file +</body></html> diff --git a/doc/im3195.html b/doc/im3195.html new file mode 100644 index 00000000..d6f2f2ed --- /dev/null +++ b/doc/im3195.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head> +<title>RFC3195 Input Module (im3195)</title> + +</head> +<body> +<h1>RFC3195 Input Module</h1> +<p><b>Module Name: im3195</b></p> +<p><b>Author: </b>Rainer Gerhards +<rgerhards@adiscon.com></p> +<p><b>Description</b>:</p> +<p>Receives syslog messages via RFC 3195. The RAW profile is fully implemented and the +COOKED profile is provided in an experimental state. This module uses +<a href="http://www.liblogging.org">liblogging</a> for the actual protocol handling.</p> +<p><b>Configuration Directives</b>:</p> +<ul> +<li><strong>$Input3195ListenPort <port></strong><br> +The port on which imklog listens for RFC 3195 messages. The default port is 601 +(the IANA-assigned port)</li> +</ul> +<b>Caveats/Known Bugs:</b> +<p>Due to no demand at all for RFC3195, we have converted rfc3195d +to this input module, but we have NOT conducted any testing. Also, +the module does not yet properly handle the recovery case. If someone +intends to put this module into production, good testing should be +cunducted. It also is a good idea to notify the rsyslog project that you intend to use +it in production. In this case, we'll probably give the module another +cleanup. We don't do this now because so far it looks just like a big +waste of time. +<p>Currently only a single listener can be defined. That one binds to all interfaces.</p> +<p><b>Sample:</b></p> +<p>The following sample accepts syslog messages via RFC 3195 on port 1601. +<br> +</p> +<textarea rows="15" cols="60">$ModLoad im3195 +$Input3195ListenPort 1601 +</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 © 2008 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/imtcp.html b/doc/imtcp.html index 2c8ead56..ecc72748 100644 --- a/doc/imtcp.html +++ b/doc/imtcp.html @@ -1,8 +1,6 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html><head> -<meta http-equiv="Content-Language" content="en"><title>TCP Syslog Input Module</title> - -</head> +<meta http-equiv="Content-Language" content="en"><title>TCP Syslog Input Module</title></head> <body> <h1>TCP Syslog Input Module</h1> <p><b>Module Name: imtcp</b></p> @@ -22,8 +20,13 @@ $InputTCPServerRun multiple times. This is not currently supported. <ul> <li>$InputTCPServerRun <port><br> Starts a TCP server on selected port</li> -<li>$InputTCPMaxSessions <number><br> -Sets the maximum number of sessions supported</li> +<li><ul><li>$InputTCPMaxSessions <number></li></ul> +Sets the maximum number of sessions supported</li><li>$InputTCPServerStreamDriverMode <number><br> +Sets the driver mode for the currently selected <a href="netstream.html">network stream driver</a>. <number> is driver specifc.</li><li>$InputTCPServerStreamDriverAuthMode <mode-string><br> +Sets the authentication mode for the currently selected <a href="netstream.html">network stream driver</a>. <mode-string> is driver specifc.</li><li>$InputTCPServerStreamDriverPermittedPeer <id-string><br> +Sets permitted peer IDs. Only these peers are able to connect to the +listener. <id-string> semantics depend on the currently selected +AuthMode and <a href="netstream.html">network stream driver</a>. PermittedPeers may not be set in anonymous modes.</li> </ul> <b>Caveats/Known Bugs:</b> <ul> diff --git a/doc/manual.html b/doc/manual.html index ec0303c3..75b521fd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -11,12 +11,12 @@ control, high precision timestamps, queued operations and the ability to filter part.</b> It is quite compatible to stock sysklogd and can be used as a drop-in replacement. Its <a href="features.html"> -advanced features</a> make it suitable for enterprise-class, <a href="rsyslog_stunnel.html">encryption protected syslog</a> +advanced features</a> make it suitable for enterprise-class, <a href="rsyslog_tls.html">encryption protected syslog</a> relay chains while at the same time being very easy to setup for the novice user. And as we know what enterprise users really need, there is also <a href="professional_support.html">professional rsyslog support</a> available directly from the source!</p> -<p><b>This documentation is for version 3.18.5 (v3-stable branch) of rsyslog.</b> +<p><b>This documentation is for version 3.19.12 (beta branch) of rsyslog.</b> Visit the <i> <a href="http://www.rsyslog.com/doc-status.html">rsyslog status page</a></i></b> to obtain current version information and project status. </p><p><b>If you like rsyslog, you might @@ -51,6 +51,7 @@ modules</a></li><li><a href="man_rsyslogd.html">rsyslogd man page</a> <ul> <li><a href="install.html">installing rsyslog</a></li> <li><a href="ipv6.html">rsyslog and IPv6</a> (which is fully supported)</li> +<li><a href="rsyslog_tls.html">native TLS encryption for syslog</a></li> <li><a href="rsyslog_stunnel.html">ssl-encrypting syslog with stunnel</a></li> <li><a href="rsyslog_mysql.html">writing syslog messages to MySQL (and other databases as well)</a></li> <li><a href="rsyslog_high_database_rate.html">writing massive amounts of syslog messages to a database</a></li> diff --git a/doc/netstream.html b/doc/netstream.html new file mode 100644 index 00000000..e7d54c12 --- /dev/null +++ b/doc/netstream.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>Network Stream Drivers</title> + +</head> +<body> +<h1>Network Stream Drivers</h1><p>Network stream drivers are a layer +between various parts of rsyslogd (e.g. the imtcp module) and the +transport layer. They provide sequenced delivery, authentication and +confidentiality to the upper layers. Drivers implement different +capabilities.</p><p> Users need to know about netstream drivers because +they need to configure the proper driver, and proper driver properties, +to achieve desired results (e.g. a <a href="rsyslog_tls.html">TLS-protected syslog transmission</a>).</p><p>The following drivers exist:</p><ul><li><a href="ns_ptcp.html">ptcp</a> - the plain tcp network transport (no security)</li><li><a href="ns_gtls.html">gtls</a> - a secure TLS transport implemented via the GnuTLS library</li></ul>[<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><font size="2">This documentation is part of the +<a href="http://www.rsyslog.com/">rsyslog</a> +project.<br> +Copyright © 2008 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>
\ No newline at end of file diff --git a/doc/ns_gtls.html b/doc/ns_gtls.html new file mode 100644 index 00000000..0d02ad02 --- /dev/null +++ b/doc/ns_gtls.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>gtls Network Stream Driver</title> + +</head> +<body> +<h1>gtls Network Stream Driver</h1> +<p>This <a href="netstream.html">network stream +driver</a> implements a TLS protected transport via the <a href="http://www.gnu.org/software/gnutls/" target="_blank">GnuTLS +library</a>.</p> +<p><b>Available since:</b> 3.19.0 (suggested minimum 3.19.8 and above)</p> +<p style="font-weight: bold;">Supported Driver Modes</p> +<ul> +<li>0 - unencrypted trasmission (just like <a href="ns_ptcp.html">ptcp</a> driver)</li> +<li>1 - TLS-protected operation</li> +</ul> +Note: mode 0 does not provide any benefit over the ptcp driver. This +mode exists for technical reasons, but should not be used. It may be +removed in the future.<br> +<span style="font-weight: bold;">Supported Authentication +Modes</span><br> +<ul> +<li><span style="font-weight: bold;">anon</span> +- anonymous authentication as +described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft</li> +<li><span style="font-weight: bold;">x509/fingerprint</span> +- certificate fingerprint authentication as +described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft</li> +<li><span style="font-weight: bold;">x509/certvalid</span> +- certificate validation only</li> +<li><span style="font-weight: bold;">x509/name</span> +- certificate validation and subject name authentication as +described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft +</li> +</ul> +Note: "anon" does not permit to authenticate the remote peer. As such, +this mode is vulnerable to man in the middle attacks as well as +unauthorized access. It is recommended NOT to use this mode.</p> +<p>x509/certvalid is a nonstandard mode. It validates the remote +peers certificate, but does not check the subject name. This is +weak authentication that may be useful in scenarios where multiple +devices are deployed and it is sufficient proof of authenticy when +their certificates are signed by the CA the server trusts. This is +better than anon authentication, but still not recommended. +<b>Known Problems</b><br> +<p>Even in x509/fingerprint mode, both the client and sever +certificate currently must be signed by the same root CA. This is an +artifact of the underlying GnuTLS library and the way we use it. It is +expected that we can resolve this issue in the future.</p> +<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 © 2008 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/ns_ptcp.html b/doc/ns_ptcp.html new file mode 100644 index 00000000..c028d6c0 --- /dev/null +++ b/doc/ns_ptcp.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>ptcp Network Stream Driver</title> + +</head> +<body> +<h1>ptcp Network Stream Driver</h1> +<p>This <a href="netstream.html">network stream driver</a> implement a plain tcp transport without security properties.</p><p>Supported Driver Modes</p><ul><li>0 - unencrypted trasmission</li></ul>Supported Authentication Modes<br><ul><li>"anon" - no authentication</li></ul>[<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><font size="2">This documentation is part of the +<a href="http://www.rsyslog.com/">rsyslog</a> +project.<br> +Copyright © 2008 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>
\ No newline at end of file diff --git a/doc/professional_support.html b/doc/professional_support.html index 1a9e6524..2cb6c1e1 100644 --- a/doc/professional_support.html +++ b/doc/professional_support.html @@ -4,11 +4,11 @@ </head> <body> -<h1>Professional Support for Rsyslog</h1> -<p>Professional Support is offered by <a href="http://www.adiscon.com">Adiscon</a>, the company -that sponsors rsyslog development. For details, please contact <a href="mailto:info%40adiscon.com">Adiscon Sales</a>.</p> -<p></p> -<h3><u>EMail Support Service</u></h3> +<h1>Professional Services for Rsyslog</h1> +<p>Professional services are being offered by <a href="http://www.adiscon.com">Adiscon</a>, the company +that sponsors rsyslog development. For details, please contact <a href="mailto:info%40adiscon.com">Adiscon Sales</a>. </p> + +<h3>EMail Support Service</h3> Price: 99.00 EURO <br> Duration: 180 days <br> @@ -19,14 +19,19 @@ need to provide proof of software support in your organization. This contract provides</p> <ul> <li>unlimited email support tickets during validity -</li><li><span style="font-weight: bold;">fixes for</span> +</li> +<li><span style="font-weight: bold;">fixes for</span> current and <span style="font-weight: bold;">past rsyslog releases</span> -</li><li>advise on how to implement rsyslog in the best possible way. -</li></ul> +</li> +<li>advise on how to implement rsyslog in the best possible +way. +</li> +</ul> <p>Under this contract, fixes for old rsyslog releases will be provided / created, provided that it is possible to do that with the -code base in question. Phone support is not included.</p><h3><u>Custom-Written Config File</u></h3> +code base in question. Phone support is not included.</p> +<h3>Custom-Written Config File</h3> Price: 29.00 EURO <br> Duration: N/A @@ -43,9 +48,35 @@ faster). For security reasons, we will not put passwords into the configuration file, but will place easy to read comments in the places where you need to put them in. The agreement is governed under German law. You may also purchase this service if you would like to have your -own configuration file reviewed, e.g. for auditing purposes.</p><br><p>All agreements are +own configuration file reviewed, e.g. for auditing purposes.</p> +<h3>Custom Development</h3> +<p>Do you need an exotic feature that otherwise would not be implemented? +Do you need something really quick, quicker than it is available via +the regular development schedule? Then, you may consider funding +development for a specific functionality. We are always looking for +interesting projects. If you hire us to to do the job, you can be sure +to get the best possible and probably quickest solution, because we are +obviously at the heart of the source code. No need to get aquainted to +anything, no risk of misunderstanding program concepts. Benefit from +our vast syslog experience.</p> +<p>Please note that custom development is not limited to rsyslog. We offer +a number of logging solutions and can also work as part of your time +for specific requirements. The opportunities are endless, just ask. We +will work with you on your requirements and provide a quote on the +estimated cost. Just write to <a href="mailto:sales@adiscon.com">sales@adiscon.com</a> for details.</p><h3>Consulting Services</h3> +<p>Do you have demanding logging requirements? Why not talk to a +real logging professional? Instead of trying to find the solution +like a needle in the haystack, talk to the team that brought rsyslog, +phpLogCon, the Windows MonitorWare products and other logging +solutions. We sweat logging for over 15 years now and can help quickly. +Depending on your needs, consulting can be carried out via email, the +phone or on your premises (for larger or local projects). Everything is +possible, it just depends on your needs. Consulting services are +available in English and German. Just mail <a href="mailto:sales@adiscon.com">sales@adiscon.com</a> what you are interested in and we will work with you on a proposal that fits your needs. +</p><p></p><p>All agreements are governed under German law. -</p><p></p> +</p> + <p>[<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> @@ -54,4 +85,4 @@ Copyright © 2008 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> +</body></html>
\ No newline at end of file diff --git a/doc/property_replacer.html b/doc/property_replacer.html index f5fc194c..367c8add 100644 --- a/doc/property_replacer.html +++ b/doc/property_replacer.html @@ -44,7 +44,13 @@ socket. Should be useful for debugging.</td> <td><b>fromhost</b></td> <td>hostname of the system the message was received from (in a relay chain, this is the system immediately in front of us and -not necessarily the original sender)</td> +not necessarily the original sender). This is a DNS-resolved name, except +if that is not possible or DNS resolution has been disabled.</td> +</tr> +<tr> +<td><b>fromhost-ip</b></td> +<td>The same as fromhost, but alsways as an IP address. Local inputs +(like imklog) use 127.0.0.1 in this property.</td> </tr> <tr> <td><b>syslogtag</b></td> @@ -198,8 +204,33 @@ not become part of it. If you are using regular expressions, the property replacer will return the part of the property text that matches the regular expression. An example for a property replacer sequence with a regular expression is: "%msg:R:.*Sev:. \(.*\) -\[.*--end%"<br> -</p> +\[.*--end%"</p> +<p>It is possible to specify some parametes after the "R". These are +comma-separated. They are: +<p>R,<regexp-type>,<submatch>,<nomatch>,<match-number> +<p>regexp-type is either "BRE" for Posix basic regular expressions or +"ERE" for extended ones. The string must be given in upper case. The +default is "BRE" to be consistent with earlier versions of rsyslog that +did not support ERE. The submatch identifies the submatch to be used +with the result. A single digit is supported. Match 0 is the full match, +while 1 to 9 are the acutal submatches. The match-number identifies which match to +use, if the expression occurs more than once inside the string. Please note +that the first match is number 0, the second 1 and so on. Up to 10 matches +(up to number 9) are supported. Please note that it would be more +natural to have the match-number in front of submatch, but this would break +backward-compatibility. So the match-number must be specified after "nomatch". +<p>nomatch is either "DFLT", "BLANK" or "FIELD" (all upper case!). It tells +what to use if no match is found. With "DFLT", the strig "**NO MATCH**" is +used. This was the only supported value up to rsyslog 3.19.5. With "BLANK" +a blank text is used (""). Finally, "FIELD" uses the full property text +instead of the expression. Some folks have requested that, so it seems +to be useful. +<p>The following is a sample of an ERE expression that takes the first +submatch from the message string and replaces the expression with +the full field if no match is found: +<p>%msg:R,ERE,1,FIELD:for (vlan[0-9]*):--end% +<p>and this takes the first submatch of the second match of said expression: +<p>%msg:R,ERE,1,FIELD,1:for (vlan[0-9]*):--end% <p><b>Also, extraction can be done based on so-called "fields"</b>. To do so, place a "F" into FromChar. A field in its current definition is anything that is delimited by a delimiter @@ -253,6 +284,10 @@ Especially useful for PIX.</td> <td>format as RFC 3339 date</td> </tr> <tr> +<td><b>date-subseconds</b></td> +<td>just the subseconds of a timestamp (always 0 for a low precision timestamp)</td> +</tr> +<tr> <td valign="top"><b>escape-cc</b></td> <td>replace control characters (ASCII value 127 and values less then 32) with an escape sequence. The sequnce is diff --git a/doc/rsyslog_conf.html b/doc/rsyslog_conf.html index 330fd51c..8f258a8b 100644 --- a/doc/rsyslog_conf.html +++ b/doc/rsyslog_conf.html @@ -50,6 +50,8 @@ input plugin for plain tcp and GSS-enable syslog</li> <li><a href="imklog.html">imklog</a> - kernel logging</li> <li><a href="imuxsock.html">imuxsock</a> - unix sockets, including the system log socket</li> +<li><a href="im3195.html">im3195</a> - +accepts syslog messages via RFC 3195</li> </ul> <p>Please note that each module provides configuration directives, which are NOT necessarily being listed below. Also @@ -114,19 +116,25 @@ default 60000 (1 minute)]</li> <li>$ActionQueueType [FixedArray/LinkedList/<b>Direct</b>/Disk]</li> <li>$ActionQueueSaveOnShutdown [on/<b>off</b>] </li> -<li>$ActionQueueWorkerThreads <number>, num -worker threads, default 1, recommended 1</li> -<li>$ActionQueueWorkerThreadMinumumMessages -<number>, default 100</li> +<li>$ActionQueueWorkerThreads <number>, num worker threads, default 1, recommended 1</li> +<li>$ActionQueueWorkerThreadMinumumMessages <number>, default 100</li> <li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li> -<li>$ActionResumeRetryCount <number> [default 0, --1 means eternal]</li> +<li>$ActionResumeRetryCount <number> [default 0, -1 means eternal]</li> +<li>$ActionSendResendLastMsgOnReconn <[on/<b>off</b>]> specifies if the last message is to be resend when a connecition broken and has been reconnedcted. May increase reliability, but comes at the risk of message duplication. +<li>$ActionSendStreamDriver <driver basename> just like $DefaultNetstreamDriver, but for the specific action +</li><li>$ActionSendStreamDriverMode <mode>, default 0, mode to use with the stream driver +(driver-specific)</li><li>$ActionSendStreamDriverAuthMode <mode>, authentication mode to use with the stream driver +(driver-specific)</li><li>$ActionSendStreamDriverPermittedPeer <ID>, accepted fingerprint (SHA1) or name of remote peer +(driver-specific) -<span style="font-weight: bold;"> directive may go away</span>!</li> <li><a href="rsconf1_allowedsender.html">$AllowedSender</a></li> <li><a href="rsconf1_controlcharacterescapeprefix.html">$ControlCharacterEscapePrefix</a></li> <li><a href="rsconf1_debugprintcfsyslinehandlerlist.html">$DebugPrintCFSyslineHandlerList</a></li> <li><a href="rsconf1_debugprintmodulelist.html">$DebugPrintModuleList</a></li> <li><a href="rsconf1_debugprinttemplatelist.html">$DebugPrintTemplateList</a></li> +<li>$DefaultNetstreamDriver <drivername>, the default <a href="netstream.html">network stream driver</a> to use. Defaults to ptcp.$DefaultNetstreamDriverCAFile </path/to/cafile.pem></li> +<li>$DefaultNetstreamDriverCertFile </path/to/certfile.pem></li> +<li>$DefaultNetstreamDriverKeyFile </path/to/keyfile.pem></li> <li><a href="rsconf1_dircreatemode.html">$DirCreateMode</a></li> <li><a href="rsconf1_dirgroup.html">$DirGroup</a></li> <li><a href="rsconf1_dirowner.html">$DirOwner</a></li> @@ -347,6 +355,10 @@ all relatively recent versions of rsyslog. Other syslogd's may get hopelessly confused if receiving that format, so check before you use it. Note that the format is unlikely to change when the final RFC comes out, but this may happen.</li> +<li><span style="font-weight: bold;">RSYSLOG_DebugFormat</span> +- a special format used for troubleshooting property problems. This format +is meant to be written to a log file. Do <b>not</b> use for production or remote +forwarding.</li> </ul> <h2>Output Channels</h2> <p>Output Channels are a new concept first introduced in rsyslog diff --git a/doc/rsyslog_mysql.html b/doc/rsyslog_mysql.html index a5c72429..753c86ec 100644 --- a/doc/rsyslog_mysql.html +++ b/doc/rsyslog_mysql.html @@ -172,7 +172,7 @@ such a password is NOT a good idea...). If your MySQL database is on the local machine, your rsyslog.conf line might look like in this sample:</p> <blockquote> -<p><code>*.* :ommysql:127.0.0.1,syslog,syslogwriter,topsecret</code></p> +<p><code>*.* :ommysql:127.0.0.1,Syslog,syslogwriter,topsecret</code></p> </blockquote> <p>Save rsyslog.conf, restart rsyslogd - and you should see syslog messages being stored in the "systemevents" table!</p> diff --git a/doc/rsyslog_ng_comparison.html b/doc/rsyslog_ng_comparison.html index aac543a7..6d14d933 100644 --- a/doc/rsyslog_ng_comparison.html +++ b/doc/rsyslog_ng_comparison.html @@ -1,11 +1,9 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head><title>rsyslog vs. syslog-ng - a comparison</title> - -</head> +<html><head><title>rsyslog vs. syslog-ng - a comparison</title></head> <body> <h1>rsyslog vs. syslog-ng</h1> <p><small><i>Written by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> -(2008-04-08)</i></small></p> +(2008-05-06)</i></small></p> <p>We have often been asked about a comparison sheet between rsyslog and syslog-ng. Unfortunately, I do not know much about syslog-ng, I did not even use it once. Also, there seems to be no @@ -57,7 +55,7 @@ comparison sheet, so please don't be shy ;)</p> </tr> <tr> <td valign="top">RFC 3195/BEEP</td> -<td valign="top">yes (needs separate build process)</td> +<td valign="top">yes (via <a href="im3195.html">im3195</a>)</td> <td valign="top">no</td> <td></td> </tr> @@ -101,7 +99,7 @@ Network (Protocol) Support</b><br> <tr> <td valign="top">support for GSS-API</td> <td valign="top">yes</td> -<td valign="top">no (?)</td> +<td valign="top">no</td> </tr> <tr> <td valign="top">ability to limit the allowed @@ -122,7 +120,7 @@ based framing on syslog/tcp connections</td> </tr> <tr> <td valign="top">syslog over RELP<br> -truly reliable message delivery (<a href="http://rgerhards.blogspot.com/2008/04/on-unreliability-of-plain-tcp-syslog.html">Why +truly reliable message delivery (<a href="http://blog.gerhards.net/2008/05/why-you-cant-build-reliable-tcp.html">Why is plain tcp syslog not reliable?</a>)</td> <td valign="top">yes</td> <td valign="top">no</td> @@ -141,20 +139,24 @@ reliable <a href="http://www.monitorware.com/Common/en/glossary/rfc3195.php">RFC <td valign="top">no</td> </tr> <tr> -<td valign="top">support for <a href="rsyslog_stunnel.html">ssl-protected +<td valign="top">support for <a href="rsyslog_tls.html">TLS/SSL-protected syslog</a> </td> -<td valign="top"><a href="rsyslog_stunnel.html">via +<td valign="top"><a href="rsyslog_tls.html">natively</a> (since 3.19.0)<br><a href="rsyslog_stunnel.html">via stunnel</a></td> <td valign="top">via stunnel<br> paid edition natively</td> </tr> <tr> -<td valign="top">support for IETF's new -syslog-protocol draft</td> +<td valign="top">support for IETF's new syslog-protocol draft</td> <td valign="top">yes</td> <td valign="top">no</td> </tr> <tr> +<td valign="top">support for IETF's new syslog-transport-tls draft</td> +<td valign="top">yes<br>(since 3.19.0 - world's first implementation)</td> +<td valign="top">no</td> +</tr> +<tr> <td valign="top">support for IPv6</td> <td valign="top">yes</td> <td valign="top">yes</td> @@ -162,7 +164,7 @@ syslog-protocol draft</td> <tr> <td valign="top">native ability to send SNMP traps</td> <td valign="top">yes</td> -<td valign="top">?</td> +<td valign="top">no</td> </tr> <tr> <td valign="top">ability to preserve the original @@ -426,9 +428,17 @@ including ability to present channel and priority as visible log data</td> <td valign="top">yes</td> <td valign="top">yes</td> </tr> +<<<<<<< HEAD:doc/rsyslog_ng_comparison.html +<tr> +<td valign="top">native ability to send mail messages</td> +<td valign="top">yes (<a href="ommail.html">ommail</a>, +introduced in 3.17.0)</td> +<td valign="top">not sure...</td> +======= <tr><td valign="top">native ability to send mail messages</td> <td valign="top">yes (<a href="ommail.html">ommail</a>, introduced in 3.17.0)</td> <td valign="top">no (only via piped external process)</td> +>>>>>>> 3f2856b4b5010dfcaa720b292dc3a655e7b9f6da:doc/rsyslog_ng_comparison.html </tr> <tr> <td valign="top">good timestamp format control; at a @@ -578,6 +588,4 @@ feature sheet. I have not yet been able to fully work through it. In the mean time, you may want to read it in parallel. It is available at <a href="http://www.balabit.com/network-security/syslog-ng/features/detailed/">Balabit's site</a>.</p> -<p>This document is current as of 2008-08-15 and definitely -incomplete (I did not yet manage to complete it!).</p> </body></html> diff --git a/doc/rsyslog_secure_tls.html b/doc/rsyslog_secure_tls.html new file mode 100644 index 00000000..be2811f4 --- /dev/null +++ b/doc/rsyslog_secure_tls.html @@ -0,0 +1,127 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: recommended scenario</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-06-17)</i></small></p> +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +<li><a href="tls_cert_errmsgs.html">Frequently seen Error Messages</a> +</ul> + +<h2>Overview</h2> +<p>This document describes a secure way to set up rsyslog TLS. A secure logging +environment requires more than just encrypting the transmission channel. This document +provides one possible way to create such a secure system. +<p>Rsyslog's TLS authentication can be used very flexible and thus supports a +wide range of security policies. This section tries to give some advise on a +scenario that works well for many environments. However, it may not be suitable +for you - please assess you security needs before using the recommendations +below. Do not blame us if it doesn't provide what you need ;)</p> +<p>Our policy offers these security benefits:</p> +<ul> + <li>syslog messages are encrypted while traveling on the wire</li> + <li>the syslog sender authenticates to the syslog receiver; thus, the + receiver knows who is talking to it</li> + <li>the syslog receiver authenticates to the syslog sender; thus, the sender + can check if it indeed is sending to the expected receiver</li> + <li>the mutual authentication prevents man-in-the-middle attacks</li> +</ul> +<p>Our secrity goals are achived via public/private key security. As such, it is +vital that private keys are well protected and not accessible to third parties. +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +I private keys have become known to third parties, the system does not provide +any security at all. Also, our solution bases on X.509 certificates and a (very +limited) chain of trust. We have one instance (the CA) that issues all machine +certificates. The machine certificate indentifies a particular machine. hile in +theory (and practice), there could be several "sub-CA" that issues machine +certificates for a specific adminitrative domain, we do not include this in our +"simple yet secure" setup. If you intend to use this, rsyslog supports it, but +then you need to dig a bit more into the documentation (or use the forum to ask). +In general, if you depart from our simple model, you should have good reasons +for doing so and know quite well what you are doing - otherwise you may +compromise your system security.</p> +<p>Please note that security never comes without effort. In the scenario +described here, we have limited the effort as much as possible. What remains is +some setup work for the central CA, the certificate setup for each machine as +well as a few configuration commands that need to be applied to all of them. +Proably the most important limiting factor in our setup is that all senders and +receivers must support IETF's syslog-transport-tls standard (which is not +finalized yet). We use mandatory-to-implement technology, yet you may have +trouble finding all required features in some implementations. More often, +unfortunately, you will find that an implementation does not support the +upcoming IETF standard at all - especially in the "early days" (starting May +2008) when rsyslog is the only implementation of said standard.</p> +<p>Fortunately, rsyslog supports allmost every protocol that is out there in the +syslog world. So in cases where transport-tls is not available on a sender, we +recommend to use rsyslog as the initial relay. In that mode, the not-capabe +sender sends to rsyslog via another protocol, which then relays the message via +transport-tls to either another interim relay or the final destination (which, +of course, must by transport-tls capable). In such a scenario, it is best to try +see what the sender support. Maybe it is possible to use industry-standard plain +tcp syslog with it. Often you can even combine it with stunnel, which then, too, +enables a secure delivery to the first rsyslog relay. If all of that is not +possible, you can (and often must...) resort to UDP. Even though this is now +lossy and insecure, this is better than not having the ability to listen to that +device at all. It may even be reasonale secure if the uncapable sender and the +first rsyslog relay communicate via a private channel, e.g. a dedicated network +link.</p> +<p>One final word of caution: transport-tls protects the connection between the +sender and the receiver. It does not necessarily protect against attacks that +are present in the message itself. Especially in a relay environment, the +message may have been originated from a malicious system, which placed invalid +hostnames and/or other content into it. If there is no provisioning against such +things, these records may show up in the receivers' repository. -transport-tls +does not protect against this (but it may help, properly used). Keep in mind +that syslog-transport-tls provides hop-by-hop security. It does not provide +end-to-end security and it does not authenticate the message itself (just the +last sender).</p> +<h3>A very quick Intro</h3> +<p>If you'd like to get all information very rapidly, the graphic below contains +everything you need to know (from the certificate perspective) in a very condensed +manner. It is no surprise if the graphic puzzles you. In this case, <a href="tls_cert_scenario.html">simply read on</a> +for full instructions. +<p> +<img align="center" alt="TLS/SSL protected syslog" src="tls_cert.jpg"> +<h3>Feedback requested</h3> +<p>I would appreciate feedback on this tutorial. If you have +additional ideas, comments or find bugs (I *do* bugs - no way... ;)), +please +<a href="mailto:rgerhards@adiscon.com">let me know</a>.</p> +<h2>Revision History</h2> +<ul> +<li>2008-06-06 * <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> * Initial Version created</li> +<li>2008-06-18 * <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> * Greatly enhanced and modularized the doc</li> +</ul> +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/rsyslog_stunnel.html b/doc/rsyslog_stunnel.html index 9d944521..104a672e 100644 --- a/doc/rsyslog_stunnel.html +++ b/doc/rsyslog_stunnel.html @@ -1,248 +1,240 @@ -<html><head>
-<title>SSL Encrypting syslog with stunnel</title>
-<meta name="KEYWORDS" content="syslog encryption, rsyslog, stunnel, secure syslog, tcp, reliable, howto, ssl">
-</head>
-<body>
-<h1>SSL Encrypting Syslog with Stunnel</h1>
- <P><small><i>Written by
- <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
- Gerhards</a> (2005-07-22)</i></small></P>
-<h2>Abstract</h2>
-<p><i><b>In this paper, I describe how to encrypt <a href="http://www.monitorware.com/en/topics/syslog/">syslog</a>
-messages on the network.</b> Encryption
-is vital to keep the confidiental content of syslog messages secure. I describe the overall
-approach and provide an HOWTO do it with the help of
-<a href="http://www.rsyslog.com">rsyslogd</a> and <a href="http://www.stunnel.org">stunnel</a>.</i></p>
-<h2>Background</h2>
-<P><b>Syslog is a
-clear-text protocol. That means anyone with a sniffer can have
-a peek at your data.</b> In some environments, this is no problem at all. In
-others, it is a huge setback, probably even preventing deployment of syslog
-solutions. Thankfully, there is an easy way to encrypt syslog communication. I
-will describe one approach in this paper.</P>
-<P>The most straigthforward solution would be that the syslogd itself encrypts
-messages. Unfortuantely, encryption is only standardized in
-<a href="http://www.monitorware.com/Common/en/glossary/rfc3195.php">RFC 3195</a>. But there
-is currently no syslogd that implements RFC 3195's encryption features,
-so this route leads to nothing. Another approach would be to use vendor- or
-project-specific syslog extensions. There are a few around, but the problem here
-is that they have compatibility issues. However, there is one surprisingly easy
-and interoperable solution: though not standardized, many vendors and projects
-implement plain tcp syslog. In a nutshell, plain tcp syslog is a mode where
-standard syslog messages are transmitted via tcp and records are separated by
-newline characters. This mode is supported by all major syslogd's (both on Linux/Unix
-and Windows) as well as log sources (for example,
-<a href="http://www.eventreporter.com/en/">EventReporter</a> for Windows
-Event Log forwarding). Plain tcp syslog offers reliability, but it does not
-offer encryption in itself. However, since it operates on a tcp stream, it is now easy
-to add encryption. There are various ways to do that. In this paper, I will
-describe how it is done with stunnel (an
-other alternative would be <a href="http://en.wikipedia.org/wiki/IPSec">IPSec</a>, for example).</P>
-<P>Stunnel is open source and it is available both for Unix/Linux and Windows.
-It provides a way to
- use ssl communication for any non-ssl aware client and server - in this case,
- our syslogd.</P>
- <P>Stunnel works much like a wrapper. Both on the client and on the server machine,
- tunnel portals are created. The non-ssl aware client and server software is
- configured to not directly talk to the remote partner, but to the local
- (s)tunnel portal instead. Stunnel, in turn, takes the data received from the
- client, encrypts it via ssl, sends it to the remote tunnel portal and that
- remote portal sends it to the recipient process on the remote machine. The
- transfer to the portals is done via unencrypted communication. As such,
- it is vital that
- the portal and the respective program that is talking to it are on the same
- machine, otherwise data would travel partly unencrypted. Tunneling, as done by stunnel,
- requires connection oriented communication. This is why you need to use
- tcp-based syslog. As a side-note, you can also encrypt a plain-text RFC
- 3195 session via stunnel, though this definitely is not what the
- protocol designers had on their mind ;)</P>
-<P>In the rest of this document, I assume that you use rsyslog on both the
-client and the server. For the samples, I use <a href="http://www.debian.org/">Debian</a>.
-Interestingly, there are
-some annoying differences between stunnel implementations. For example, on
-Debian a comment line starts with a semicolon (';'). On
-<a href="http://www.redhat.com">Red Hat</a>, it starts with
-a hash sign ('#'). So you need to watch out for subtle issues when setting up
-your system.</P>
-<h2>Overall System Setup</h2>
-<P>In ths paper, I assume two machines, one named "client" and the other named "server".
-It is obvious that, in practice, you will probably have multiple clients but
-only one server. Syslog traffic shall be transmitted via stunnel over the
-network. Port 60514 is to be used for that purpose. The machines are set up as
-follows:</P>
-<P><b>Client</b></P>
-<ul>
- <li>rsyslog forwards message to stunnel local portal at port 61514</li>
- <li>local stunnel forwards data via the network to port 60514 to its remote
- peer</li>
-</ul>
-<P><b>Server</b></P>
-<ul>
- <li>stunnel listens on port 60514 to connections from its client peers</li>
- <li>all connections are forwarded to the locally-running rsyslog listening
- at port 61514</li>
-</ul>
-<h2>Setting up the system</h2>
-<P>For Debian, you need the "stunnel4" package. The "stunnel" package is the
-older 3.x release, which will not support the configuration I describe below.
-Other distributions might have other names. For example, on Red Hat it is just "stunnel".
-Make sure that you install the appropriate package on both the client and the
-server. It is also a good idea to check if there are updates for either stunnel
-or openssl (which stunnel uses) - there are often security fixes available and
-often the latest fixes are not included in the default package.</P>
-<P>In my sample setup, I use only the bare minimum of options. For example, I do
-not make the server check client cerficiates. Also, I do not talk much about
-certificates at all. If you intend to really secure your system, you should
-probably learn about certificates and how to manage and deploy them. This is
-beyond the scope of this paper. For additional information,
-<a href="http://www.stunnel.org/faq/certs.html">
-http://www.stunnel.org/faq/certs.html</a> is a good starting point.</P>
-<P>You also need to install rsyslogd on both machines. Do this before starting
-with the configuration. You should also familarize yourself with its
-configuration file syntax, so that you know which actions you can trigger with
-it. Rsyslogd can work as a drop-in replacement for stock
-<a href="http://www.infodrom.org/projects/sysklogd/">sysklogd</a>. So if you know
-the standard syslog.conf syntax, you do not need to learn any more to follow
-this paper.</P>
-<h3>Server Setup</h3>
-<P>At the server, you need to have a digital certificate. That certificate
-enables SSL operation, as it provides the necessary crypto keys being used to
-secure the connection. Many versions of stunnel come with a default certificate,
-often found in /etc/stunnel/stunnel.pem. If you have it, it is good for testing
-only. If you use it in production, it is very easy to break into your secure
-channel as everybody is able to get hold of your private key. I didn't find an
-stunnel.pem on my Debian machine. I guess the Debian folks removed it because of
-its insecurity.</P>
-<P>You can create your own certificate with a simple openssl tool - you need to
-do it if you have none and I highly recommend to create one in any case. To
-create it, cd to /etc/stunnel and type:</P>
-<p><blockquote><code>openssl req -new -x509 -days 3650 -nodes -out
-stunnel.pem -keyout stunnel.pem</code></blockquote></p>
-<P>That command will ask you a number of questions. Provide some answer for
-them. If you are unsure, read
-<a href="http://www.stunnel.org/faq/certs.html">
-http://www.stunnel.org/faq/certs.html</a>. After the command has finished, you
-should have a usable stunnel.pem in your working directory.</P>
-<P>Next is to create a configuration file for stunnel. It will direct stunnel
-what to do. You can used the following basic file:</P>
-<P><blockquote><code><pre>; Certificate/key is needed in server mode
-cert = /etc/stunnel/stunnel.pem
-
-<i>; Some debugging stuff useful for troubleshooting
-debug = 7
-foreground=yes</i>
-
-[ssyslog]
-accept = 60514
-connect = 61514</pre>
-</code></blockquote></P>
-<p>Save this file to e.g. /etc/stunnel/syslog-server.conf. Please note that the
-settings in <i>italics</i> are for debugging only. They run stunnel
-with a lot of debug information in the foreground. This is very valuable while
-you setup the system - and very useless once everything works well. So be sure
-to remove these lines when going to production.</p>
-<p>Finally, you need to start the stunnel daemon. Under Debian, this is done via
-"stunnel /etc/stunnel/syslog.server.conf". If you have enabled the debug
-settings, you will immediately see a lot of nice messages.</p>
-<p>Now you have stunnel running, but it obviously unable to talk to rsyslog -
-because it is not yet running. If not already done, configure it so that it does
-everything you want. If in doubt, you can simply copy /etc/syslog.conf to /etc/rsyslog.conf
-and you probably have what you want. The really important thing in rsyslogd
-configuration is that you must make it listen to tcp port 61514 (remember: this
-is where stunnel send the messages to). Thankfully, this is easy to achive: just
-add "-t 61514" to the rsyslogd startup options in your system startup script.
-After done so, start (or restart) rsyslogd.</p>
-<p>The server should now be fully operational.</p>
-<h3>Client Setup</h3>
-<P>The client setup is simpler. Most importantly, you do not need a certificate
-(of course, you can use one if you would like to authenticate the client, but
-this is beyond the scope of this paper). So the basic thing you need to do is
-create the stunnel configuration file.</P>
-<P><blockquote><code><pre><i>; Some debugging stuff useful for troubleshooting
-debug = 7
-foreground=yes</i>
-
-<b>client=yes</b>
-
-[ssyslog]
-accept = 127.0.0.1:61514
-connect = <font color="#FF0000">192.0.2.1</font>:60514
-</pre>
-</code></blockquote></P>
-<P>Again, the text in <i>italics</i> is for debugging purposes only. I suggest
-you leave it in during your initial testing and then remove it. The most
-important difference to the server configuration outlined above is the "client=yes"
-directive. It is what makes this stunnel behave like a client. The accept
-directive binds stunnel only to the local host, so that it is protected from
-receiving messages from the network (somebody might fake to be the local sender).
-The address "192.0.2.1" is the address of the server machine. You must change it
-to match your configuration. Save this file to /etc/stunnel/syslog-client.conf.</P>
-<P>Then, start stunnel via "stunnel4 /etc/stunnel/syslog-client.conf". Now
-you should see some startup messages. If no errors appear, you have a running
-client stunnel instance.</P>
-<P>Finally, you need to tell rsyslogd to send data to the remote host. In stock
-syslogd, you do this via the "@host" forwarding directive. The same works with
-rsyslog, but it suppports extensions to use tcp. Add the following line to your
-/etc/rsyslog.conf:</P>
-<P><blockquote><code><pre>*.* @<font color="#FF0000">@</font>127.0.0.1:61514
-</pre>
-</code></blockquote><i></P>
-
-</i>
-
-<P>Please note the double at-sign (@@). This is no typo. It tells rsyslog to use
-tcp instead of udp delivery. In this sample, all messages are forwarded to the
-remote host. Obviously, you may want to limit this via the usual rsyslog.conf
-settings (if in doubt, use man rsyslog.con).</P>
-<P>You do not need to add any special startup settings to rsyslog on the client.
-Start or restart rsyslog so that the new configuration setting takes place.</P>
-<h3>Done</h3>
-<P>After following these steps, you should have a working secure syslog
-forwarding system. To verify, you can type "logger test" or a similar smart
-command on the client. It should show up in the respective server log file. If
-you dig out you sniffer, you should see that the traffic on the wire is actually
-protected. In the configuration use above, the two stunnel endpoints should be
-quite chatty, so that you can follow the action going on on your system.</P>
-<P>If you have only basic security needs, you can probably just remove the debug
-settings and take the rest of the configuration to production. If you are
-security-sensitve, you should have a look at the various stunnel settings that
-help you further secure the system.</P>
-<h2>Preventing Systems from talking directly to the rsyslog Server</h2>
-<P>It is possible that remote systems (or attackers) talk to the rsyslog server
-by directly connecting to its port 61514. Currently (July of 2005), rsyslog does
-not offer the ability to bind to the local host, only. This feature is planned,
-but as long as it is missing, rsyslog must be protected via a firewall. This can
-easily be done via e.g iptables. Just be sure not to forget it.</P>
-<h2>Conclusion</h2>
-<P>With minumal effort, you can set up a secure logging infrastructure employing
-ssl encrypted syslog message transmission. As a side note, you also have the
-benefit of reliable tcp delivery which is far less prone to message loss than
-udp.</P>
-<h3>Feedback requested</h3>
-<P>I would appreciate feedback on this tutorial. If you have additional ideas,
-comments or find bugs (I *do* bugs - no way... ;)), please
-<a href="mailto:rgerhards@adiscon.com">let me know</a>.</P>
-<h2>Revision History</h2>
-<ul>
- <li>2005-07-22 *
- <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> * Initial Version created</li>
- <li>2005-07-26 *
- <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> * Some text brush-up, hyperlinks added</li>
- <li>2005-08-03 *
- <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a>
- * license added</li>
-</ul>
-<h2>Copyright</h2>
-<p>Copyright (c) 2005
-<a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> and
-<a href="http://www.adiscon.com/en/">Adiscon</a>.</p>
-<p> Permission is granted to copy, distribute and/or modify this document
- under the terms of the GNU Free Documentation License, Version 1.2
- or any later version published by the Free Software Foundation;
- with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- Texts. A copy of the license can be viewed at
-<a href="http://www.gnu.org/copyleft/fdl.html">
-http://www.gnu.org/copyleft/fdl.html</a>.</p>
-
-</body>
-</html>
\ No newline at end of file +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head> + +<title>SSL Encrypting syslog with stunnel</title><meta name="KEYWORDS" content="syslog encryption, rsyslog, stunnel, secure syslog, tcp, reliable, howto, ssl"></head><body> +<h1>SSL Encrypting Syslog with Stunnel</h1> + <p><small><i>Written by + <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer + Gerhards</a> (2005-07-22)</i></small></p> +<h2>Abstract</h2> +<p><i><b>In this paper, I describe how to encrypt <a href="http://www.monitorware.com/en/topics/syslog/">syslog</a> +messages on the network.</b> Encryption +is vital to keep the confidiental content of syslog messages secure. I describe the overall +approach and provide an HOWTO do it with the help of +<a href="http://www.rsyslog.com">rsyslogd</a> and <a href="http://www.stunnel.org">stunnel</a>.</i></p><p><span style="font-weight: bold;">Please note that starting with rsyslog 3.19.0, </span><a style="font-weight: bold;" href="rsyslog_tls.html">rsyslog provides native TLS/SSL encryption</a><span style="font-weight: bold;"> <span style="font-style: italic;">without</span> the need of stunnel. </span>I +strongly recomend to use that feature instead of stunnel. The stunnel +documentation here is mostly provided for backwards compatibility. New +deployments are advised to use native TLS mode.<i></i></p> +<h2>Background</h2> +<p><b>Syslog is a +clear-text protocol. That means anyone with a sniffer can have +a peek at your data.</b> In some environments, this is no problem at all. In +others, it is a huge setback, probably even preventing deployment of syslog +solutions. Thankfully, there is an easy way to encrypt syslog communication. I +will describe one approach in this paper.</p> +<p>The most straigthforward solution would be that the syslogd itself encrypts +messages. Unfortuantely, encryption is only standardized in +<a href="http://www.monitorware.com/Common/en/glossary/rfc3195.php">RFC 3195</a>. But there +is currently no syslogd that implements RFC 3195's encryption features, +so this route leads to nothing. Another approach would be to use vendor- or +project-specific syslog extensions. There are a few around, but the problem here +is that they have compatibility issues. However, there is one surprisingly easy +and interoperable solution: though not standardized, many vendors and projects +implement plain tcp syslog. In a nutshell, plain tcp syslog is a mode where +standard syslog messages are transmitted via tcp and records are separated by +newline characters. This mode is supported by all major syslogd's (both on Linux/Unix +and Windows) as well as log sources (for example, +<a href="http://www.eventreporter.com/en/">EventReporter</a> for Windows +Event Log forwarding). Plain tcp syslog offers reliability, but it does not +offer encryption in itself. However, since it operates on a tcp stream, it is now easy +to add encryption. There are various ways to do that. In this paper, I will +describe how it is done with stunnel (an +other alternative would be <a href="http://en.wikipedia.org/wiki/IPSec">IPSec</a>, for example).</p> +<p>Stunnel is open source and it is available both for Unix/Linux and Windows. +It provides a way to + use ssl communication for any non-ssl aware client and server - in this case, + our syslogd.</p> + <p>Stunnel works much like a wrapper. Both on the client and on the server machine, + tunnel portals are created. The non-ssl aware client and server software is + configured to not directly talk to the remote partner, but to the local + (s)tunnel portal instead. Stunnel, in turn, takes the data received from the + client, encrypts it via ssl, sends it to the remote tunnel portal and that + remote portal sends it to the recipient process on the remote machine. The + transfer to the portals is done via unencrypted communication. As such, + it is vital that + the portal and the respective program that is talking to it are on the same + machine, otherwise data would travel partly unencrypted. Tunneling, as done by stunnel, + requires connection oriented communication. This is why you need to use + tcp-based syslog. As a side-note, you can also encrypt a plain-text RFC + 3195 session via stunnel, though this definitely is not what the + protocol designers had on their mind ;)</p> +<p>In the rest of this document, I assume that you use rsyslog on both the +client and the server. For the samples, I use <a href="http://www.debian.org/">Debian</a>. +Interestingly, there are +some annoying differences between stunnel implementations. For example, on +Debian a comment line starts with a semicolon (';'). On +<a href="http://www.redhat.com">Red Hat</a>, it starts with +a hash sign ('#'). So you need to watch out for subtle issues when setting up +your system.</p> +<h2>Overall System Setup</h2> +<p>In ths paper, I assume two machines, one named "client" and the other named "server". +It is obvious that, in practice, you will probably have multiple clients but +only one server. Syslog traffic shall be transmitted via stunnel over the +network. Port 60514 is to be used for that purpose. The machines are set up as +follows:</p> +<p><b>Client</b></p> +<ul> + <li>rsyslog forwards message to stunnel local portal at port 61514</li> + <li>local stunnel forwards data via the network to port 60514 to its remote + peer</li> +</ul> +<p><b>Server</b></p> +<ul> + <li>stunnel listens on port 60514 to connections from its client peers</li> + <li>all connections are forwarded to the locally-running rsyslog listening + at port 61514</li> +</ul> +<h2>Setting up the system</h2> +<p>For Debian, you need the "stunnel4" package. The "stunnel" package is the +older 3.x release, which will not support the configuration I describe below. +Other distributions might have other names. For example, on Red Hat it is just "stunnel". +Make sure that you install the appropriate package on both the client and the +server. It is also a good idea to check if there are updates for either stunnel +or openssl (which stunnel uses) - there are often security fixes available and +often the latest fixes are not included in the default package.</p> +<p>In my sample setup, I use only the bare minimum of options. For example, I do +not make the server check client cerficiates. Also, I do not talk much about +certificates at all. If you intend to really secure your system, you should +probably learn about certificates and how to manage and deploy them. This is +beyond the scope of this paper. For additional information, +<a href="http://www.stunnel.org/faq/certs.html"> +http://www.stunnel.org/faq/certs.html</a> is a good starting point.</p> +<p>You also need to install rsyslogd on both machines. Do this before starting +with the configuration. You should also familarize yourself with its +configuration file syntax, so that you know which actions you can trigger with +it. Rsyslogd can work as a drop-in replacement for stock +<a href="http://www.infodrom.org/projects/sysklogd/">sysklogd</a>. So if you know +the standard syslog.conf syntax, you do not need to learn any more to follow +this paper.</p> +<h3>Server Setup</h3> +<p>At the server, you need to have a digital certificate. That certificate +enables SSL operation, as it provides the necessary crypto keys being used to +secure the connection. Many versions of stunnel come with a default certificate, +often found in /etc/stunnel/stunnel.pem. If you have it, it is good for testing +only. If you use it in production, it is very easy to break into your secure +channel as everybody is able to get hold of your private key. I didn't find an +stunnel.pem on my Debian machine. I guess the Debian folks removed it because of +its insecurity.</p> +<p>You can create your own certificate with a simple openssl tool - you need to +do it if you have none and I highly recommend to create one in any case. To +create it, cd to /etc/stunnel and type:</p> +<p></p><blockquote><code>openssl req -new -x509 -days 3650 -nodes -out +stunnel.pem -keyout stunnel.pem</code></blockquote><p></p> +<p>That command will ask you a number of questions. Provide some answer for +them. If you are unsure, read +<a href="http://www.stunnel.org/faq/certs.html"> +http://www.stunnel.org/faq/certs.html</a>. After the command has finished, you +should have a usable stunnel.pem in your working directory.</p> +<p>Next is to create a configuration file for stunnel. It will direct stunnel +what to do. You can used the following basic file:</p> +<p></p><blockquote><code></code><pre>; Certificate/key is needed in server mode<br>cert = /etc/stunnel/stunnel.pem<br><br><i>; Some debugging stuff useful for troubleshooting<br>debug = 7<br>foreground=yes</i> + +[ssyslog] +accept = 60514 +connect = 61514</pre> +</blockquote><p></p> +<p>Save this file to e.g. /etc/stunnel/syslog-server.conf. Please note that the +settings in <i>italics</i> are for debugging only. They run stunnel +with a lot of debug information in the foreground. This is very valuable while +you setup the system - and very useless once everything works well. So be sure +to remove these lines when going to production.</p> +<p>Finally, you need to start the stunnel daemon. Under Debian, this is done via +"stunnel /etc/stunnel/syslog.server.conf". If you have enabled the debug +settings, you will immediately see a lot of nice messages.</p> +<p>Now you have stunnel running, but it obviously unable to talk to rsyslog - +because it is not yet running. If not already done, configure it so that it does +everything you want. If in doubt, you can simply copy /etc/syslog.conf to /etc/rsyslog.conf +and you probably have what you want. The really important thing in rsyslogd +configuration is that you must make it listen to tcp port 61514 (remember: this +is where stunnel send the messages to). Thankfully, this is easy to achive: just +add "-t 61514" to the rsyslogd startup options in your system startup script. +After done so, start (or restart) rsyslogd.</p> +<p>The server should now be fully operational.</p> +<h3>Client Setup</h3> +<p>The client setup is simpler. Most importantly, you do not need a certificate +(of course, you can use one if you would like to authenticate the client, but +this is beyond the scope of this paper). So the basic thing you need to do is +create the stunnel configuration file.</p> +<p></p><blockquote><code></code><pre><i>; Some debugging stuff useful for troubleshooting<br>debug = 7<br>foreground=yes</i> + +<b>client=yes</b> + +[ssyslog] +accept = 127.0.0.1:61514 +connect = <font color="#ff0000">192.0.2.1</font>:60514<br></pre> +</blockquote><p></p> +<p>Again, the text in <i>italics</i> is for debugging purposes only. I suggest +you leave it in during your initial testing and then remove it. The most +important difference to the server configuration outlined above is the "client=yes" +directive. It is what makes this stunnel behave like a client. The accept +directive binds stunnel only to the local host, so that it is protected from +receiving messages from the network (somebody might fake to be the local sender). +The address "192.0.2.1" is the address of the server machine. You must change it +to match your configuration. Save this file to /etc/stunnel/syslog-client.conf.</p> +<p>Then, start stunnel via "stunnel4 /etc/stunnel/syslog-client.conf". Now +you should see some startup messages. If no errors appear, you have a running +client stunnel instance.</p> +<p>Finally, you need to tell rsyslogd to send data to the remote host. In stock +syslogd, you do this via the "@host" forwarding directive. The same works with +rsyslog, but it suppports extensions to use tcp. Add the following line to your +/etc/rsyslog.conf:</p> +<p></p><blockquote><code></code><pre>*.* @<font color="#ff0000">@</font>127.0.0.1:61514<br></pre> +</blockquote><i><p></p> + +</i> + +<p>Please note the double at-sign (@@). This is no typo. It tells rsyslog to use +tcp instead of udp delivery. In this sample, all messages are forwarded to the +remote host. Obviously, you may want to limit this via the usual rsyslog.conf +settings (if in doubt, use man rsyslog.con).</p> +<p>You do not need to add any special startup settings to rsyslog on the client. +Start or restart rsyslog so that the new configuration setting takes place.</p> +<h3>Done</h3> +<p>After following these steps, you should have a working secure syslog +forwarding system. To verify, you can type "logger test" or a similar smart +command on the client. It should show up in the respective server log file. If +you dig out you sniffer, you should see that the traffic on the wire is actually +protected. In the configuration use above, the two stunnel endpoints should be +quite chatty, so that you can follow the action going on on your system.</p> +<p>If you have only basic security needs, you can probably just remove the debug +settings and take the rest of the configuration to production. If you are +security-sensitve, you should have a look at the various stunnel settings that +help you further secure the system.</p> +<h2>Preventing Systems from talking directly to the rsyslog Server</h2> +<p>It is possible that remote systems (or attackers) talk to the rsyslog server +by directly connecting to its port 61514. Currently (July of 2005), rsyslog does +not offer the ability to bind to the local host, only. This feature is planned, +but as long as it is missing, rsyslog must be protected via a firewall. This can +easily be done via e.g iptables. Just be sure not to forget it.</p> +<h2>Conclusion</h2> +<p>With minumal effort, you can set up a secure logging infrastructure employing +ssl encrypted syslog message transmission. As a side note, you also have the +benefit of reliable tcp delivery which is far less prone to message loss than +udp.</p> +<h3>Feedback requested</h3> +<p>I would appreciate feedback on this tutorial. If you have additional ideas, +comments or find bugs (I *do* bugs - no way... ;)), please +<a href="mailto:rgerhards@adiscon.com">let me know</a>.</p> +<h2>Revision History</h2> +<ul> + <li>2005-07-22 * + <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> * Initial Version created</li> + <li>2005-07-26 * + <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> * Some text brush-up, hyperlinks added</li> + <li>2005-08-03 * + <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> + * license added</li><li>2008-05-05 * <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> + * updated to reflect native TLS capability of rsyslog 3.19.0 and above</li> +</ul> +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html"> +http://www.gnu.org/copyleft/fdl.html</a>.</p> + +</body></html>
\ No newline at end of file diff --git a/doc/rsyslog_tls.html b/doc/rsyslog_tls.html new file mode 100644 index 00000000..7d156c3a --- /dev/null +++ b/doc/rsyslog_tls.html @@ -0,0 +1,307 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS (SSL) Encrypting syslog</title> + +<meta name="KEYWORDS" content="syslog encryption, rsyslog, secure syslog, tcp, reliable, howto, ssl, tls"> +</head> +<body> +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-05-06)</i></small></p> +<h2>Abstract</h2> +<p><i><b>In this paper, I describe how to encrypt <a href="http://www.monitorware.com/en/topics/syslog/">syslog</a> +messages on the network.</b> Encryption +is vital to keep the confidiental content of syslog messages secure. I +describe the overall +approach and provide an HOWTO do it with <a href="http://www.rsyslog.com">rsyslog's</a> TLS +features. </i></p> +<p>Please +note that TLS is the more secure successor of SSL. While people often +talk about "SSL encryption" they actually mean "TLS encryption". So +don't look any further if you look for how to SSL-encrypt syslog. You +have found the right spot.</p> +<p>This is a quick guide. There is a more elaborate guide currently +under construction which provides a much more secure environment. It +is highly recommended to +<a href="rsyslog_secure_tls.html">at least have a look at it</a>. +<h2>Background</h2> +<p><b>Traditional syslog is a clear-text protocol. That +means anyone with a sniffer can have a peek at your data.</b> In +some environments, this is no problem at all. In others, it is a huge +setback, probably even preventing deployment of syslog solutions. +Thankfully, there are easy ways to encrypt syslog +communication. </p> +The traditional approach involves <a href="rsyslog_stunnel.html">running +a wrapper like stunnel around the syslog session</a>. This works +quite well and is in widespread use. However, it is not thightly +coupled with the main syslogd and some, even severe, problems can +result from this (follow a mailing list thread that describes <a href="http://lists.adiscon.net/pipermail/rsyslog/2008-March/000580.html">total +loss of syslog messages due to stunnel mode</a> and the <a href="http://rgerhards.blogspot.com/2008/04/on-unreliability-of-plain-tcp-syslog.html">unreliability +of TCP syslog</a>). +<p><a href="gssapi.html">Rsyslog supports syslog via +GSSAP</a>I since long to overcome these limitatinos. However, +syslog via GSSAPI is a rsyslog-exclusive transfer mode and it requires +a proper Kerberos environment. As such, it isn't a really universal +solution. The <a href="http://www.ietf.org/">IETF</a> +has begun standardizing syslog over plain tcp over +TLS for a while now. While I am not fully satisfied with the results so +far, this obviously has the potential to become the long-term +solution. The Internet Draft in question, syslog-transport-tls has been +dormant for some time but is now (May of 2008) again being worked on. I +expect it to turn into a RFC within the next 12 month (but don't take +this for granted ;)). I didn't want to wait for it, because there +obviously is need for TLS syslog right now (and, honestly, I have +waited long enough...). Consequently, I have +implemented the current draft, with some interpretations I made (there +will be a compliance doc soon). So in essence, a TLS-protected syslog +transfer mode is available right now. As a side-note, Rsyslog +is the world's first +implementation of syslog-transport-tls.</p> +<p>Please note that in theory it should be compatible with other, +non IETF syslog-transport-tls implementations. If you would like to run +it with something else, please let us know so that we can create a +compatibility list (and implement compatbility where it doesn't yet +exist). </p> +<h2>Overall System Setup</h2> +<p>Encryption requires a reliable stream. So It will not work +over UDP syslog. In rsyslog, network transports utilize a so-called +"network stream layer" (netstream for short). This layer provides a +unified view of the transport to the application layer. The plain TCP +syslog sender and receiver are the upper layer. The driver layer +currently consists of the "ptcp" and "gtls" library plugins. "ptcp" +stands for "plain tcp" and is used for unencrypted message transfer. It +is also used internally by the gtls driver, so it must always be +present on a system. The "gtls" driver is for GnutTLS, a TLS library. +It is used for encrypted message transfer. In the future, additional +drivers will become available (most importantly, we would like to +include a driver for NSS).</p> +<p>What you need to do to build an encrypted syslog channel is to +simply use the proper netstream drivers on both the client and the +server. Client, in the sense of this document, is the rsyslog system +that is sending syslog messages to a remote (central) loghost, which is +called the server. In short, the setup is as follows:</p> +<p><b>Client</b></p> +<ul> +<li>forwards messages via plain tcp syslog using gtls netstream +driver to central sever on port 10514<br> +</li> +</ul> +<p><b>Server</b></p> +<ul> +<li>accept incoming messages via plain tcp syslog using gtls +netstream driver on port 10514</li> +</ul> +<h2>Setting up the system</h2> +<h3>Server Setup</h3> +<p>At the server, you need to have a digital certificate. That +certificate enables SSL operation, as it provides the necessary crypto +keys being used to secure the connection. There is a set of default +certificates in ./contrib/gnutls. These are key.pem and cert.pem. These +are good for testing. If you use it in production, +it is very easy to break into your secure channel as everybody is able +to get hold of your private key. So it is a good idea to +generate the key and certificate yourself.</p> +<p>You also need a root CA certificate. Again, there is a sample +CA certificate in ./contrib/gnutls, named ca.cert. It is suggested to +generate your own.</p> +<p>To configure the server, you need to tell it where are its +certificate files, to use the gtls driver and start up a listener. This +is done as follows:<br> +</p> +<blockquote><code></code> +<pre># make gtls driver the default +$DefaultNetstreamDriver gtls + +# certificate files +$DefaultNetstreamDriverCAFile /path/to/contrib/gnutls/ca.pem +$DefaultNetstreamDriverCertFile /path/to/contrib/gnutls/cert.pem +$DefaultNetstreamDriverKeyFile /path/to/contrib/gnutls/key.pem + +$ModLoad /home/rger/proj/rsyslog/plugins/imtcp/.libs/imtcp # load listener + +$InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode +$InputTCPServerStreamDriverAuthMode anon # client is NOT authenticated +$InputTCPServerRun 10514 # start up listener at port 10514 +</pre> +</blockquote> +This is all you need to do. You can use the rest of your rsyslog.conf +together with this configuration. The way messages are received does +not interfer with any other option, so you are able to do anything else +you like without any restrictions. +<p>Restart (or HUP) rsyslogd. The server should now be fully +operational.</p> +<h3>Client Setup</h3> +<p>The client setup is equally simple. You need less +certificates, just the CA cert. </p> +<blockquote> +<pre># certificate files - just CA for a client +$DefaultNetstreamDriverCAFile /path/to/contrib/gnutls/ca.pem + +# set up the action +$DefaultNetstreamDriver gtls # use gtls netstream driver +$ActionSendStreamDriverMode 1 # require TLS for the connection +$ActionSendStreamDriverAuthMode anon # server is NOT authenticated +*.* @@(o)server.example.net:10514 # send (all) messages + +</pre> +</blockquote> +<p>Note that we use the regular TCP forwarding syntax (@@) here. +There is nothing special, because the encryption is handled by the +netstream driver. So I have just forwarded every message (*.*) for +simplicity - you can use any of rsyslog's filtering capabilities (like +epxression-based filters or regular expressions). Note that the "(o)" +part is not strictly necessary. It selects octet-based framing, which +provides compatiblity to IETF's syslog-transport-tls draft. Besides +compatibility, this is also a more reliable transfer mode, so I suggest +to always use it.</p> +<h3>Done</h3> +<p>After +following these steps, you should have a working secure +syslog forwarding system. To verify, you can type "logger test" or a +similar "smart" command on the client. It should show up in the +respective server log file. If you dig out your sniffer, you should see +that the traffic on the wire is actually protected.</p> +<h3>Limitations</h3> +<p>The current implementation has a number of limitations. These +are +being worked on. Most importantly, neither the client nor the server +are authenticated. So while the message transfer is encrypted, you can +not be sure which peer you are talking to. Please note that this is a +limitation found in most real-world SSL syslog systems. Of course, that +is not an excuse for not yet providing this feature - but it tells you +that it is acceptable and can be worked around by proper firewalling, +ACLs and other organizational measures. Mutual authentication will be +added shortly to rsyslog.</p> +<p>Secondly, the plain tcp syslog listener +can currently listen to a single port, in a single mode. So if you use +a TLS-based listener, you can not run unencrypted syslog on the same +instance at the same time. A work-around is to run a second rsyslogd +instance. This limitation, too, is scheduled to be removed soon.</p> +<p>The +RELP transport can currently not be protected by TLS. A work-around is +to use stunnel. TLS support for RELP will be added once plain TCP +syslog has sufficiently matured.</p> +<h2>Certificates</h2> +<p>In order to be really secure, certificates are needed. This is +a short summary on how to generate the necessary certificates with +GnuTLS' certtool. You can also generate certificates via other tools, +but as we currently support GnuTLS as the only TLS library, we thought +it is a good idea to use their tools.<br> +</p> +<p>Note that this section aims at people who are not involved +with PKI at all. The main goal is to get them going in a reasonable +secure way. </p> +<h3>CA Certificate</h3> +<p>This is used to sign all of your other certificates. The CA +cert must be trusted by all clients and servers. The private key must +be well-protected and not given to any third parties. The certificate +itself can (and must) be distributed. To generate it, do the following:</p> +<ol> +<li>generate the private key: +<pre>certtool --generate-privkey --outfile ca-key.pem</pre> +<br> +This takes a short while. Be sure to do some work on your workstation, +it waits for radom input. Switching between windows is sufficient ;) +</li> +<li>now create the (self-signed) CA certificate itself:<br> +<pre>certtool --generate-self-signed --load-privkey ca-key.pem --outfile ca.pem</pre> +This generates the CA certificate. This command queries you for a +number of things. Use appropriate responses. When it comes to +certificate validity, keep in mind that you need to recreate all +certificates when this one expires. So it may be a good idea to use a +long period, eg. 3650 days (roughly 10 years). You need to specify that +the certificates belongs to an authrity. The certificate is used to +sign other certificates.<br> +</li> +<li>You need to distribute this certificate +to all peers and you need to point to it via the +$DefaultNetstreamDriverCAFile config directive. All other certificates +will be issued by this CA.<br> +Important: do only distribute the ca.pem, NOT ca-key.pem (the private +key). Distributing the CA private key would totally breach security as +everybody could issue new certificates on the behalf of this CA. +</li> +</ol> +<h3>Individual Peer Certificate</h3> +<p>Each peer (be it client, server or both), needs a certificate +that conveys its identity. Access control is based on these +certificates. You can, for example, configure a server to accept +connections only from configured clients. The client ID is taken from +the client instances certificate. So as a general rule of thumb, you +need to create a certificate for each instance of rsyslogd that you +run. That instance also needs the private key, so that it can properly +decrypt the traffic. Safeguard the peer's private key file. If somebody +gets hold of it, it can malicously pretend to be the compromised host. +If such happens, regenerate the certificate and make sure you use a +different name instead of the compromised one (if you use name-based +authentication). </p> +<p>These are the steps to generate the indivudual certificates +(repeat: you need to do this for every instance, do NOT share the +certificates created in this step):</p> +<ol> +<li>generate a private key (do NOT mistake this with the CA's +private key - this one is different):<br> +<pre>certtool --generate-privkey --outfile key.pem</pre> +Again, this takes a short while.</li> +<li>generate a certificate request:<br> +<pre>certtool --generate-request --load-privkey key.pem --outfile request.pem</pre> +If you do not have the CA's private key (because you are not authorized +for this), you can send the certificate request to the responsible +person. If you do this, you can skip the remaining steps, as the CA +will provide you with the final certificate. If you submit the request +to the CA, you need to tell the CA the answers that you would normally +provide in step 3 below. +</li> +<li>Sign (validate, authorize) the certificate request and +generate the instances certificate. You need to have the CA's +certificate and private key for this:<br> +<pre>certtool --generate-certificate --load-request request.pem --outfile cert.pem \<br> --load-ca-certificate ca.pem --load-ca-privkey ca-key.pem</pre> +Answer questions as follows: Cert does not belogn to an authority; it +is a TLS web server and client certificate; the dnsName MUST be the +name of the peer in question (e.g. centralserver.example.net) - this is +the name used for authenticating the peers. Please note that you may +use an IP address in dnsName. This is a good idea if you would like to +use default server authentication and you use selector lines with IP +addresses (e.g. "*.* @@192.168.0.1") - in that case you need to select +a dnsName of 192.168.0.1. But, of course, changing the server IP then +requires generating a new certificate.</li> +</ol> +After you have generated the certificate, you need to place it onto the +local machine running rsyslogd. Specify the certificate and key via the +$DefaultNetstreamDriverCertFile /path/to/cert.pem and +$DefaultNetstreamDriverKeyFile /path/to/key.pem configuration +directives. Make sure that nobody has access to key.pem, as that would +breach security. And, once again: do NOT use these files on more than +one instance. Doing so would prevent you from distinguising between the +instances and thus would disable useful authentication. +<h3>Troubleshooting Certificates</h3> +<p>If you experience trouble with your certificate setup, it may +be +useful to get some information on what is contained in a specific +certificate (file). To obtain that information, do </p> +<pre>$ certtool --certificate-info --infile cert.pem</pre> +<p>where "cert.pem" can be replaced by the various certificate pem files (but it does not work with the key files).</p> +<h2>Conclusion</h2> +<p>With minumal effort, you can set up a secure logging +infrastructure employing TLS encrypted syslog message transmission.</p> +<h3>Feedback requested</h3> +<p>I would appreciate feedback on this tutorial. If you have +additional ideas, comments or find bugs (I *do* bugs - no way... ;)), +please +<a href="mailto:rgerhards@adiscon.com">let me know</a>.</p> +<h2>Revision History</h2> +<ul> +<li>2008-05-06 * <a href="http://www.gerhards.net/rainer">Rainer +Gerhards</a> * Initial Version created</li><li>2008-05-26 * <a href="http://www.gerhards.net/rainer">Rainer +Gerhards</a> * added information about certificates</li> +</ul> +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/queueWorkerLogic.dia b/doc/src/queueWorkerLogic.dia Binary files differindex 068ea50c..068ea50c 100644 --- a/doc/queueWorkerLogic.dia +++ b/doc/src/queueWorkerLogic.dia diff --git a/doc/src/tls.dia b/doc/src/tls.dia Binary files differnew file mode 100644 index 00000000..77e5d185 --- /dev/null +++ b/doc/src/tls.dia diff --git a/doc/src/tls_cert.dia b/doc/src/tls_cert.dia Binary files differnew file mode 100644 index 00000000..e76431df --- /dev/null +++ b/doc/src/tls_cert.dia diff --git a/doc/src/tls_cert_100.dia b/doc/src/tls_cert_100.dia Binary files differnew file mode 100644 index 00000000..baed5e0f --- /dev/null +++ b/doc/src/tls_cert_100.dia diff --git a/doc/src/tls_cert_ca.dia b/doc/src/tls_cert_ca.dia Binary files differnew file mode 100644 index 00000000..7ce27a8d --- /dev/null +++ b/doc/src/tls_cert_ca.dia diff --git a/doc/status.html b/doc/status.html index 63a3f588..90932fca 100644 --- a/doc/status.html +++ b/doc/status.html @@ -2,22 +2,24 @@ <html><head><title>rsyslog status page</title></head> <body> <h2>rsyslog status page</h2> -<p>This page reflects the status as of 2008-04-15.</p> +<p>This page reflects the status as of 2008-07-15.</p> <h2>Current Releases</h2> -<p><b>development:</b> 3.17.1 - -<a href="http://www.rsyslog.com/Article213.phtml">change log</a> - -<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-98.phtml">download</a> +<!-- no devel at this time! +<p><b>development:</b> 3.19.9 [2008-07-07] - +<a href="http://www.rsyslog.com/Article250.phtml">change log</a> - +<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-117.phtml">download</a> +--> -<br><b>beta:</b> 3.15.1 - -<a href="http://www.rsyslog.com/Article210.phtml">change log</a> - -<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-97.phtml">download</a></p> +<br><b>beta:</b> 3.19.10 [2008-07-15] - +<a href="http://www.rsyslog.com/Article256.phtml">change log</a> - +<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-109.phtml">download</a></p> -<p><b>v3 stable:</b> 3.14.2 - <a href="http://www.rsyslog.com/Article209.phtml">change log</a> - -<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-96.phtml">download</a> +<p><b>v3 stable:</b> 3.18.0 [2008-07-11] - <a href="http://www.rsyslog.com/Article254.phtml">change log</a> - +<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-120.phtml">download</a> -<br><b>v2 stable:</b> 2.0.4 - <a href="http://www.rsyslog.com/Article197.phtml">change log</a> - -<a href="http://www.rsyslog.com/Downloads-index-req-getit-lid-90.phtml">download</a> +<br><b>v2 stable:</b> 2.0.5 [2008-05-15] - <a href="http://www.rsyslog.com/Article226.phtml">change log</a> - +<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-104.phtml">download</a> <br>v0 and v1 are deprecated and no longer supported. If you absolutely do not like to upgrade, you may consider purchasing a <a href="professional_support.html">commercial rsyslog support package</a>. Just let us point diff --git a/doc/tls_cert.jpg b/doc/tls_cert.jpg Binary files differnew file mode 100644 index 00000000..920e998d --- /dev/null +++ b/doc/tls_cert.jpg diff --git a/doc/tls_cert_100.jpg b/doc/tls_cert_100.jpg Binary files differnew file mode 100644 index 00000000..beeedc58 --- /dev/null +++ b/doc/tls_cert_100.jpg diff --git a/doc/tls_cert_ca.html b/doc/tls_cert_ca.html new file mode 100644 index 00000000..2cae4040 --- /dev/null +++ b/doc/tls_cert_ca.html @@ -0,0 +1,168 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: scenario</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-06-17)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +</ul> + +<h3>Setting up the CA</h3> +<p>The first step is to set up a certificate authority (CA). It must be +maintained by a trustworthy person (or group) and approves the indentities of +all machines. It does so by issuing their certificates. In a small setup, the +administrator can provide the CA function. What is important is the the CA's +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +private key is well-protocted and machine certificates are only issued if it is +know they are valid (in a single-admin case that means the admin should not +issue certificates to anyone else except himself).</p> +<p>The CA creates a so-called self-signed certificate. That is, it approves its +own authenticy. This sounds useless, but the key point to understand is that +every machine will be provided a copy of the CA's certificate. Accepting this +certificate is a matter of trust. So by configuring the CA certificate, the +administrator tells <a href="http://www.rsyslog.com">rsyslog</a> which certificates to trust. This is the root of all +trust under this model. That is why the CA's private key is so important - +everyone getting hold of it is trusted by our rsyslog instances.</p> +<center><img src="tls_cert_ca.jpg"></center> +<p>To create a self-signed certificate, use the following commands with GnuTLS (which +is currently the only supported TLS library, what may change in the future). +Please note that GnuTLS' tools are not installed by default on many platforms. Also, +the tools do not necessarily come with the GnuTLS core package. If you do not +have certtool on your system, check if there is package for the GnuTLS tools available +(under Fedora, for example, this is named gnutls-utils-<version> and +it is NOT installed by default). </p> +<ol> +<li>generate the private key: +<pre>certtool --generate-privkey --outfile ca-key.pem</pre> +<br> +This takes a short while. Be sure to do some work on your workstation, +it waits for radom input. Switching between windows is sufficient ;) +</li> +<li>now create the (self-signed) CA certificate itself:<br> +<pre>certtool --generate-self-signed --load-privkey ca-key.pem --outfile ca.pem</pre> +This generates the CA certificate. This command queries you for a +number of things. Use appropriate responses. When it comes to +certificate validity, keep in mind that you need to recreate all +certificates when this one expires. So it may be a good idea to use a +long period, eg. 3650 days (roughly 10 years). You need to specify that +the certificates belongs to an authority. The certificate is used to +sign other certificates.<br> +</li> +</ol> +<h3>Sample Screen Session</h3> +<p>Text in red is user input. Please note that for some questions, there is no +user input given. This means the default was accepted by simply pressing the +enter key. +<code><pre> +[root@rgf9dev sample]# <font color="red">certtool --generate-privkey --outfile ca-key.pem --bits 2048</font> +Generating a 2048 bit RSA private key... +[root@rgf9dev sample]# <font color="red">certtool --generate-self-signed --load-privkey ca-key.pem --outfile ca.pem</font> +Generating a self signed certificate... +Please enter the details of the certificate's distinguished name. Just press enter to ignore a field. +Country name (2 chars): <font color="red">US</font> +Organization name: <font color="red">SomeOrg</font> +Organizational unit name: <font color="red">SomeOU</font> +Locality name: <font color="red">Somewhere</font> +State or province name: <font color="red">CA</font> +Common name: <font color="red">someName (not necessarily DNS!)</font> +UID: +This field should not be used in new certificates. +E-mail: +Enter the certificate's serial number (decimal): + + +Activation/Expiration time. +The certificate will expire in (days): <font color="red">3650</font> + + +Extensions. +Does the certificate belong to an authority? (Y/N): <font color="red">y</font> +Path length constraint (decimal, -1 for no constraint): +Is this a TLS web client certificate? (Y/N): +Is this also a TLS web server certificate? (Y/N): +Enter the e-mail of the subject of the certificate: <font color="red">someone@example.net</font> +Will the certificate be used to sign other certificates? (Y/N): <font color="red">y</font> +Will the certificate be used to sign CRLs? (Y/N): +Will the certificate be used to sign code? (Y/N): +Will the certificate be used to sign OCSP requests? (Y/N): +Will the certificate be used for time stamping? (Y/N): +Enter the URI of the CRL distribution point: +X.509 Certificate Information: + Version: 3 + Serial Number (hex): 485a365e + Validity: + Not Before: Thu Jun 19 10:35:12 UTC 2008 + Not After: Sun Jun 17 10:35:25 UTC 2018 + Subject: C=US,O=SomeOrg,OU=SomeOU,L=Somewhere,ST=CA,CN=someName (not necessarily DNS!) + Subject Public Key Algorithm: RSA + Modulus (bits 2048): + d9:9c:82:46:24:7f:34:8f:60:cf:05:77:71:82:61:66 + 05:13:28:06:7a:70:41:bf:32:85:12:5c:25:a7:1a:5a + 28:11:02:1a:78:c1:da:34:ee:b4:7e:12:9b:81:24:70 + ff:e4:89:88:ca:05:30:0a:3f:d7:58:0b:38:24:a9:b7 + 2e:a2:b6:8a:1d:60:53:2f:ec:e9:38:36:3b:9b:77:93 + 5d:64:76:31:07:30:a5:31:0c:e2:ec:e3:8d:5d:13:01 + 11:3d:0b:5e:3c:4a:32:d8:f3:b3:56:22:32:cb:de:7d + 64:9a:2b:91:d9:f0:0b:82:c1:29:d4:15:2c:41:0b:97 + Exponent: + 01:00:01 + Extensions: + Basic Constraints (critical): + Certificate Authority (CA): TRUE + Subject Alternative Name (not critical): + RFC822name: someone@example.net + Key Usage (critical): + Certificate signing. + Subject Key Identifier (not critical): + fbfe968d10a73ae5b70d7b434886c8f872997b89 +Other Information: + Public Key Id: + fbfe968d10a73ae5b70d7b434886c8f872997b89 + +Is the above information ok? (Y/N): <font color="red">y</font> + + +Signing certificate... +[root@rgf9dev sample]# <font color="red">chmod 400 ca-key.pem</font> +[root@rgf9dev sample]# <font color="red">ls -l</font> +total 8 +-r-------- 1 root root 887 2008-06-19 12:33 ca-key.pem +-rw-r--r-- 1 root root 1029 2008-06-19 12:36 ca.pem +[root@rgf9dev sample]# +</pre></code> +<p><font color="red"><b>Be sure to safeguard ca-key.pem!</b> Nobody except the CA itself +needs to have it. If some third party obtains it, you security is broken!</font> +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_ca.jpg b/doc/tls_cert_ca.jpg Binary files differnew file mode 100644 index 00000000..f2da0454 --- /dev/null +++ b/doc/tls_cert_ca.jpg diff --git a/doc/tls_cert_client.html b/doc/tls_cert_client.html new file mode 100644 index 00000000..dbe7961b --- /dev/null +++ b/doc/tls_cert_client.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: client setup</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-07-03)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +</ul> + +<h3>Setting up a client</h3> +<p>In this step, we configure a client machine. We from our scenario, we use +zuse.example.net. You need to do the same steps for all other clients, too (in the +example, that meanst turng.example.net). The client check's the server's identity and +talks to it only if it is the expected server. This is a very important step. +Without it, you would not detect man-in-the-middle attacks or simple malicious servers +who try to get hold of your valuable log data. +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +<p><center><img src="tls_cert_100.jpg"></center> +<p>Steps to do: +<ul> +<li>make sure you have a functional CA (<a href="tls_cert_ca.html">Setting up the CA</a>) +<li>generate a machine certificate for zuse.example.net (follow instructions in + <a href="tls_cert_machine.html">Generating Machine Certificates</a>) +<li>make sure you copy over ca.pem, machine-key.pem ad machine-cert.pem to the client. +Ensure that no user except root can access them (<b>even read permissions are really bad</b>). +<li>configure the client so that it checks the server identity and sends messages only +if the server identity is known. Please note that you have the same options as when +configuring a server. However, we now use a single name only, because there is only one +central server. No using wildcards make sure that we will exclusively talk to that server +(otherwise, a compromised client may take over its role). If you load-balance to different +server identies, you obviously need to allow all of them. It still is suggested to use +explcit names. +</ul> +<p><b>At this point, please be reminded once again that your security needs may be quite different from +what we assume in this tutorial. Evaluate your options based on your security needs.</b> +<h3>Sample syslog.conf</h3> +<p>Keep in mind that this rsyslog.conf sends messages via TCP, only. Also, we do not +show any rules to write local files. Feel free to add them. +<code><pre> +# make gtls driver the default +$DefaultNetstreamDriver gtls + +# certificate files +$DefaultNetstreamDriverCAFile /rsyslog/protected/ca.pem +$DefaultNetstreamDriverCertFile /rsyslog/protected/machine-cert.pem +$DefaultNetstreamDriverKeyFile /rsyslog/protected/machine-key.pem + +$ActionSendStreamDriverAuthMode x509/name +$ActionSendStreamDriverPermittedPeer central.example.net +$ActionSendStreamDriverMode 1 # run driver in TLS-only mode +*.* @@central.example.net:10514 # forward everything to remote server +</pre></code> +<p>Note: the example above forwards every message to the remote server. Of course, +you can use the normal filters to restrict the set of information that is sent. +Depending on your message volume and needs, this may be a smart thing to do. +<p><font color="red"><b>Be sure to safeguard at least the private key (machine-key.pem)!</b> +If some third party obtains it, you security is broken!</font> +<h2>Copyright</h2> +<p>Copyright © 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_errmsgs.html b/doc/tls_cert_errmsgs.html new file mode 100644 index 00000000..d002174c --- /dev/null +++ b/doc/tls_cert_errmsgs.html @@ -0,0 +1,103 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: error messages</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-06-17)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +<li><a href="tls_cert_errmsgs.html">Frequently seen Error Messages</a> +</ul> + +<h3>Error Messages</h3> +<p>This page covers error message you may see when setting up +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +<a href="http://www.rsyslog.com">rsyslog</a> with TLS. Please note that many +of the message stem back to the TLS library being used. In those cases, there is +not always a good explanation available in rsyslog alone. +<p>A single error typically results in two or more message being emitted: (at +least) one is the actual error cause, followed by usually one message with additional +information (like certificate contents). In a typical system, these message should +immediately follow each other in your log. Kepp in mind that they are reported +as syslog.err, so you need to capture these to actually see errors (the default +rsyslog.conf's shipped by many systems will do that, recording them e.g. in +/etc/messages). +<h3>certificate invalid</h3> +<p>Sample: +<code> +not permitted to talk to peer, certificate invalid: <font color="red">insecure algorithm</font> +</code> +<p>This message may occur during connection setup. It indicates that the remote peer's +certificate can not be accepted. The reason for this is given in the message part that +is shown in red. Please note that this red part directly stems back to the TLS library, +so rsyslog does acutally not have any more information about the reason. +<p>With GnuTLS, the following reasons have been seen in practice: +<h4>insecure algorith</h4> +<p>The certificate contains information on which encryption algorithms are to be used. +This information is entered when the certificate is created. +Some older alogrithms are no longer secure and the TLS library does not accept +them. Thus the connection request failed. The cure is to use a certificate with sufficiently secure +alogorithms. +<p>Please note that noi encryption algorithm is totally secure. It only is secure based +on our current knowledge AND on computing power available. As computers get more and more +powerful, previously secure algorithms become insecure over time. As such, algorithms +considered secure today may not be accepted by the TLS library in the future. +<p>So in theory, after a system upgrade, a connection request may fail with the "insecure +algorithm" failure without any change in rsyslog configuration or certificates. This could be +caused by a new perception of the TLS library of what is secure and what not. +<h3>GnuTLS error -64</h3> +<p>Sample: <code>unexpected GnuTLS error -64 in nsd_gtls.c:517: Error while reading file.</code> +<p>This error points to an encoding error witht the pem file in question. It means "base 64 encoding error". +From my experience, it can be caused by a couple of things, some of them not obvious: +<ul> +<li>You specified a wrong file, which is not actually in .pem format +<li>The file was incorrectly generated +<li>I think I have also seen this when I accidently swapped private key files and +certificate files. So double-check the type of file you are using. +<li>It may even be a result of an access (permission) problem. In theory, that +should lead to another error, but in practice it sometimes seems to lead to +this -64 error. +</ul> +<h3>info on invalid cert</h3> +<p>Sample: +<code> +info on invalid cert: peer provided 1 certificate(s). Certificate 1 info: certificate valid from Wed Jun 18 11:45:44 2008 to Sat Jun 16 11:45:53 2018; Certificate public key: RSA; DN: C=US,O=Sample Corp,OU=Certs,L=Somehwere,ST=CA,CN=somename; Issuer DN: C=US,O=Sample Corp,OU=Certs,L=Somewhere,ST=CA,CN=somename,EMAIL=xxx@example.com; SAN:DNSname: machine.example.net; +</code> +<p>This is <b>not</b> an error message in itself. It always follows the actual error message and +tells you what is seen in the peer's certificate. This is done to give you a chance to evaluate +the certificate and better understand why the initial error message was issued. +<p>Please note that you can NOT diagnose problems based on this message alone. It follows +in a number of error cases and does not pinpoint any problems by itself. +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_machine.html b/doc/tls_cert_machine.html new file mode 100644 index 00000000..5ecde0d1 --- /dev/null +++ b/doc/tls_cert_machine.html @@ -0,0 +1,172 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: generating the machine certificate</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-06-18)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +</ul> + +<h3>generating the machine certificate</h3> +<p>In this step, we generate certificates for each of the machines. Please note +that both clients and servers need certificates. The certificate identifies each +machine to the remote peer. The DNSName specified inside the certificate can +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +be specified inside the $<object>PermittedPeer config statements. +<p>For now, we assume that a single person (or group) is responsible for the whole +rsyslog system and thus it is OK if that single person is in posession of all +machine's private keys. This simplification permits us to use a somewhat less +complicated way of generating the machine certificates. So, we generate both the private +and public key on the CA (which is NOT a server!) and then copy them over to the +respective machines. +<p>If the roles of machine and CA administrators are split, the private key must +be generated by the machine administrator. This is done via a certificate request. +This request is then sent to the CA admin, which in turn generates the certificate +(containing the public key). The CA admin then sends back the certificate to the +machine admin, who installs it. That way, the CA admin never get's hold of the +machine's private key. Instructions for this mode will be given in a later revision +of this document. +<p><b>In any case, it is vital that the machine's private key is protected. Anybody +able to obtain that private key can imporsonate as the machine to which it belongs, thus +breaching your security.</b> +<h3>Sample Screen Session</h3> +<p>Text in red is user input. Please note that for some questions, there is no +user input given. This means the default was accepted by simply pressing the +enter key. +<p><b>Please note:</b> you need to substitute the names specified below with values +that match your environment. Most importantly, machine.example.net must be replaced +by the actual name of the machine that will be using this certificate. For example, +if you generate a certificate for a machine named "server.example.com", you need +to use that name. If you generate a certificate for "client.example.com", you need +to use this name. Make sure that each machine certificate has a unique name. If not, +you can not apply proper access control. +<code><pre> +[root@rgf9dev sample]# <font color="red">certtool --generate-privkey --outfile key.pem --bits 2048</font> +Generating a 2048 bit RSA private key... +[root@rgf9dev sample]# <font color="red">certtool --generate-request --load-privkey key.pem --outfile request.pem</font> +Generating a PKCS #10 certificate request... +Country name (2 chars): <font color="red">US</font> +Organization name: <font color="red">SomeOrg</font> +Organizational unit name: <font color="red">SomeOU</font> +Locality name: <font color="red">Somewhere</font> +State or province name: <font color="red">CA</font> +Common name: <font color="red">machine.example.net</font> +UID: +Enter a challenge password: +[root@rgf9dev sample]# <font color="red">certtool --generate-certificate --load-request request.pem --outfile cert.pem --load-ca-certificate ca.pem --load-ca-privkey ca-key.pem</font> +Generating a signed certificate... +Enter the certificate's serial number (decimal): + + +Activation/Expiration time. +The certificate will expire in (days): 1000 + + +Extensions. +Does the certificate belong to an authority? (Y/N): <font color="red">n</font> +Is this a TLS web client certificate? (Y/N): <font color="red">y</font> +Is this also a TLS web server certificate? (Y/N): <font color="red">y</font> +Enter the dnsName of the subject of the certificate: <font color="red">machine.example.net</font> <i>{This is the name of the machine that will use the certificate}</i> +Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (Y/N): +Will the certificate be used for encryption (RSA ciphersuites)? (Y/N): +X.509 Certificate Information: + Version: 3 + Serial Number (hex): 485a3819 + Validity: + Not Before: Thu Jun 19 10:42:54 UTC 2008 + Not After: Wed Mar 16 10:42:57 UTC 2011 + Subject: C=US,O=SomeOrg,OU=SomeOU,L=Somewhere,ST=CA,CN=machine.example.net + Subject Public Key Algorithm: RSA + Modulus (bits 2048): + b2:4e:5b:a9:48:1e:ff:2e:73:a1:33:ee:d8:a2:af:ae + 2f:23:76:91:b8:39:94:00:23:f2:6f:25:ad:c9:6a:ab + 2d:e6:f3:62:d8:3e:6e:8a:d6:1e:3f:72:e5:d8:b9:e0 + d0:79:c2:94:21:65:0b:10:53:66:b0:36:a6:a7:cd:46 + 1e:2c:6a:9b:79:c6:ee:c6:e2:ed:b0:a9:59:e2:49:da + c7:e3:f0:1c:e0:53:98:87:0d:d5:28:db:a4:82:36:ed + 3a:1e:d1:5c:07:13:95:5d:b3:28:05:17:2a:2b:b6:8e + 8e:78:d2:cf:ac:87:13:15:fc:17:43:6b:15:c3:7d:b9 + Exponent: + 01:00:01 + Extensions: + Basic Constraints (critical): + Certificate Authority (CA): FALSE + Key Purpose (not critical): + TLS WWW Client. + TLS WWW Server. + Subject Alternative Name (not critical): + DNSname: machine.example.net + Subject Key Identifier (not critical): + 0ce1c3dbd19d31fa035b07afe2e0ef22d90b28ac + Authority Key Identifier (not critical): + fbfe968d10a73ae5b70d7b434886c8f872997b89 +Other Information: + Public Key Id: + 0ce1c3dbd19d31fa035b07afe2e0ef22d90b28ac + +Is the above information ok? (Y/N): <font color="red">y</font> + + +Signing certificate... +[root@rgf9dev sample]# <font color="red">rm -f request.pem</font> +[root@rgf9dev sample]# <font color="red">ls -l</font> +total 16 +-r-------- 1 root root 887 2008-06-19 12:33 ca-key.pem +-rw-r--r-- 1 root root 1029 2008-06-19 12:36 ca.pem +-rw-r--r-- 1 root root 1074 2008-06-19 12:43 cert.pem +-rw-r--r-- 1 root root 887 2008-06-19 12:40 key.pem +[root@rgf9dev sample]# # it may be a good idea to rename the files to indicate where they belong to +[root@rgf9dev sample]# <font color="red">mv cert.pem machine-cert.pem</font> +[root@rgf9dev sample]# <font color="red">mv key.pem machine-key.pem</font> +[root@rgf9dev sample]# +</pre></code> +<h3>Distributing Files</h3> +<p>Provide the machine with: +<ul> +<li>a copy of ca.pem +<li>cert.pem +<li>key.pem +</ul> +<p>This is how the relevant part of rsyslog.conf looks on the target machine: +<p> +<code><pre> +$DefaultNetstreamDriverCAFile /home/rger/proj/rsyslog/sample/ca.pem +$DefaultNetstreamDriverCertFile /home/rger/proj/rsyslog/sample/machine-cert.pem +$DefaultNetstreamDriverKeyFile /home/rger/proj/rsyslog/sample/machine-key.pem +</pre></code> +<p><b><font color="red">Never</font> provide anyone with ca-key.pem!</b> Also, make sure +nobody but the machine in question gets hold of key.pem. +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_scenario.html b/doc/tls_cert_scenario.html new file mode 100644 index 00000000..7973532b --- /dev/null +++ b/doc/tls_cert_scenario.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: scenario</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-06-17)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +<li><a href="tls_cert_errmsgs.html">Frequently seen Error Messages</a> +</ul> + +<h3>Sample Scenario</h3> +<p>We have a quite simple scenario. There is one central syslog server, +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +named central.example.net. These server is being reported to by two Linux +machines with name zuse.example.net and turing.example.net. Also, there is a +third client - ada.example.net - which send both its own messages to the central +server but also forwards messages receive from an UDP-only capable router. We +hav decided to use ada.example.net because it is in the same local network +segment as the router and so we enjoy TLS' security benefits for forwarding the +router messages inside the corporate network. All systems (except the router) use +<a href="http://www.rsyslog.com/">rsyslog</a> as the syslog software.</p> +<p><center><img src="tls_cert_100.jpg"></center> +<p>Please note that the CA must not necessarily be connected to the rest of the +network. Actually, it may be considered a security plus if it is not. If the CA +is reachable via the regular network, it should be sufficiently secured (firewal +rules et al). Keep in mind that if the CA's security is breached, your overall +system security is breached. +<p>In case the CA is compromised, you need to regenerate the CA's certificate as well +as all individual machines certificates. +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_server.html b/doc/tls_cert_server.html new file mode 100644 index 00000000..51ad7bed --- /dev/null +++ b/doc/tls_cert_server.html @@ -0,0 +1,118 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: central server setup</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-06-18)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +</ul> + +<h3>Setting up the Central Server</h3> +<p>In this step, we configure the central server. We assume it accepts messages only +via TLS protected plain tcp based syslog from those peers that are explicitely permitted +to send to it. The picture below show our configuration. This step configures +the server central.example.net. +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +<p><center><img src="tls_cert_100.jpg"></center> +<p>Steps to do: +<ul> +<li>make sure you have a functional CA (<a href="tls_cert_ca.html">Setting up the CA</a>) +<li>generate a machine certificate for central.example.net (follow instructions in + <a href="tls_cert_machine.html">Generating Machine Certificates</a>) +<li>make sure you copy over ca.pem, machine-key.pem ad machine-cert.pem to the central server. +Ensure that no user except root can access them (<b>even read permissions are really bad</b>). +<li>configure the server so that it accepts messages from all machines in the +example.net domain that have certificates from your CA. Alternatively, you may also +precisely define from which machine names messages are accepted. See sample rsyslog.conf +below. +</ul> +In this setup, we use wildcards to ease adding new systems. We permit the server to accept +messages from systems whos names match *.example.net. +<pre><code> +$InputTCPServerStreamDriverPermittedPeer *.example.net +</code></pre> +This will match zuse.example.net and +turing.example.net, but NOT pascal.otherdepartment.example.net. If the later would be desired, +you can (and need) to include additional permitted peer config statments: +<pre><code> +$InputTCPServerStreamDriverPermittedPeer *.example.net +$InputTCPServerStreamDriverPermittedPeer *.otherdepartment.example.net +$InputTCPServerStreamDriverPermittedPeer *.example.com +</code></pre> +<p>As can be seen with example.com, the different permitted peers need NOT to be in a single +domain tree. Also, individual machines can be configured. For example, if only zuse, turing +and ada should be able to talk to the server, you can achive this by: +<pre><code> +$InputTCPServerStreamDriverPermittedPeer zuse.example.net +$InputTCPServerStreamDriverPermittedPeer turing.example.net +$InputTCPServerStreamDriverPermittedPeer ada.example.net +</code></pre> +<p>As an extension to the (upcoming) IETF syslog/tls standard, you can specify some text +together with a domain component wildcard. So "*server.example.net", "server*.example.net" +are valid permitted peers. However "server*Fix.example.net" is NOT a valid wildcard. The +IETF standard permits no text along the wildcards. +<p>The reason we use wildcards in the default setup is that it makes it easy to add systems +without the need to change the central server's configuration. It is important to understand that +the central server will accept names <b>only</b> (no exception) if the client certificate was +signed by the CA we set up. So if someone tries to create a malicious certificate with +a name "zuse.example.net", the server will <b>not</b> accept it. So a wildcard is safe +as long as you ensure CA security is not breached. Actually, you authorize a client by issuing +the certificate to it. +<p><b>At this point, please be reminded once again that your security needs may be quite different from +what we assume in this tutorial. Evaluate your options based on your security needs.</b> +<h3>Sample syslog.conf</h3> +<p>Keep in mind that this rsyslog.conf accepts messages via TCP, only. The only other +source accepted is messages from the server itself. +<code><pre> +$ModLoad /home/rger/proj/rsyslog/plugins/imuxsock/.libs/imuxsock # local messages +$ModLoad /home/rger/proj/rsyslog/plugins/imtcp/.libs/imtcp + +# make gtls driver the default +$DefaultNetstreamDriver gtls + +# certificate files +$DefaultNetstreamDriverCAFile /rsyslog/protected/ca.pem +$DefaultNetstreamDriverCertFile /rsyslog/protected/machine-cert.pem +$DefaultNetstreamDriverKeyFile /rsyslog/protected/machine-key.pem + +$InputTCPServerStreamDriverAuthMode x509/name +$InputTCPServerStreamDriverPermittedPeer *.example.net +$InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode +$InputTCPServerRun 10514 # start up listener at port 10514 +</pre></code> +<p><font color="red"><b>Be sure to safeguard at least the private key (machine-key.pem)!</b> +If some third party obtains it, you security is broken!</font> +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_summary.html b/doc/tls_cert_summary.html new file mode 100644 index 00000000..8e003bc8 --- /dev/null +++ b/doc/tls_cert_summary.html @@ -0,0 +1,66 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: Summary</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-07-03)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +</ul> + +<h3>Summary</h3> +<p>If you followed the steps outlined in this documentation set, you now have +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +a reasonable (for most needs) secure setup for the following environment: +<center><img src="tls_cert_100.jpg"></center> +<p>You have learned about the security decisions involved and which we +made in this example. <b>Be once again reminded that you must make sure yourself +that whatever you do matches your security needs!</b> There is no guarantee that +what we generally find useful actually is. It may even be totally unsuitable for +your environment. +<p>In the example, we created a rsyslog certificate authority (CA). Guard the CA's +files. You need them whenever you need to create a new machine certificate. We also saw how +to generate the machine certificates themselfs and distribute them to the individual +machines. Also, you have found some configuration samples for a sever, a client and +a syslog relay. Hopefully, this will enable you to set up a similar system in many +environments. +<p>Please be warned that you defined some expiration dates for the certificates. +After they are reached, the certificates are no longer valid and rsyslog will NOT +accept them. At that point, syslog messages will no longer be transmitted (and rsyslogd +will heavily begin to complain). So it is a good idea to make sure that you renew the +certificates before they expire. Recording a reminder somewhere is probably a good +idea. +<p>If you have any more questions, please visit the <a href="http://kb.monitorware.com/rsyslog-f40.html">rsyslog forum</a> and simply ask ;) +<h2>Copyright</h2> +<p>Copyright (c) 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> diff --git a/doc/tls_cert_udp_relay.html b/doc/tls_cert_udp_relay.html new file mode 100644 index 00000000..f4740ce7 --- /dev/null +++ b/doc/tls_cert_udp_relay.html @@ -0,0 +1,105 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head><title>TLS-protected syslog: UDP relay setup</title> +</head> +<body> + +<h1>Encrypting Syslog Traffic with TLS (SSL)</h1> +<p><small><i>Written by <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> (2008-07-03)</i></small></p> + +<ul> +<li><a href="rsyslog_secure_tls.html">Overview</a> +<li><a href="tls_cert_scenario.html">Sample Scenario</a> +<li><a href="tls_cert_ca.html">Setting up the CA</a> +<li><a href="tls_cert_machine.html">Generating Machine Certificates</a> +<li><a href="tls_cert_server.html">Setting up the Central Server</a> +<li><a href="tls_cert_client.html">Setting up syslog Clients</a> +<li><a href="tls_cert_udp_relay.html">Setting up the UDP syslog relay</a> +<li><a href="tls_cert_summary.html">Wrapping it all up</a> +</ul> + +<h3>Setting up the UDP syslog relay</h3> +<p>In this step, we configure the UDP relay ada.example.net. +As a reminder, that machine relays messages from a local router, which only +supports UDP syslog, to the central syslog server. The router does not talk +directly to it, because we would like to have TLS protection for its sensitve +logs. If the router and the syslog relay are on a sufficiently secure private +network, this setup can be considered reasonable secure. In any case, it is the +best alternative among the possible configuration scenarios. +<span style="float: left"> +<script type="text/javascript"><!-- +google_ad_client = "pub-3204610807458280"; +/* rsyslog doc inline */ +google_ad_slot = "5958614527"; +google_ad_width = 125; +google_ad_height = 125; +//--> +</script> +<script type="text/javascript" +src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> +</script> +</span> +<p><center><img src="tls_cert_100.jpg"></center> +<p>Steps to do: +<ul> +<li>make sure you have a functional CA (<a href="tls_cert_ca.html">Setting up the CA</a>) +<li>generate a machine certificate for ada.example.net (follow instructions in + <a href="tls_cert_machine.html">Generating Machine Certificates</a>) +<li>make sure you copy over ca.pem, machine-key.pem ad machine-cert.pem to the client. +Ensure that no user except root can access them (<b>even read permissions are really bad</b>). +<li>configure the client so that it checks the server identity and sends messages only +if the server identity is known. +</ul> +<p>These were essentially the same steps as for any +<a href="tls_cert_client.html">TLS syslog client</a>. We now need to add the +capability to forward the router logs: +<ul> +<li>make sure that the firewall rules permit message recpetion on UDP port 514 (if you use +a non-standard port for UDP syslog, make sure that port number is permitted). +<li>you may want to limit who can send syslog messages via UDP. A great place to do this +is inside the firewall, but you can also do it in rsyslog.conf via an $AllowedSender +directive. We have used one in the sample config below. Please be aware that this is +a kind of weak authentication, but definitely better than nothing... +<li>add the UDP input plugin to rsyslog's config and start a UDP listener +<li>make sure that your forwarding-filter permits to forward messages received +from the remote router to the server. In our sample scenario, we do not need to +add anything special, because all messages are forwarded. This includes messages +received from remote hosts. +</ul> +<p><b>At this point, please be reminded once again that your security needs may be quite different from +what we assume in this tutorial. Evaluate your options based on your security needs.</b> +<h3>Sample syslog.conf</h3> +<p>Keep in mind that this rsyslog.conf sends messages via TCP, only. Also, we do not +show any rules to write local files. Feel free to add them. +<code><pre> +# start a UDP listener for the remote router +$ModLoad imudp # load UDP server plugin +$AllowedSender UDP, 192.0.2.1 # permit only the router +$UDPServerRun 514 # listen on default syslog UDP port 514 + +# make gtls driver the default +$DefaultNetstreamDriver gtls + +# certificate files +$DefaultNetstreamDriverCAFile /rsyslog/protected/ca.pem +$DefaultNetstreamDriverCertFile /rsyslog/protected/machine-cert.pem +$DefaultNetstreamDriverKeyFile /rsyslog/protected/machine-key.pem + +$ActionSendStreamDriverAuthMode x509/name +$ActionSendStreamDriverPermittedPeer central.example.net +$ActionSendStreamDriverMode 1 # run driver in TLS-only mode +*.* @@central.example.net:10514 # forward everything to remote server +</pre></code> +<p><font color="red"><b>Be sure to safeguard at least the private key (machine-key.pem)!</b> +If some third party obtains it, you security is broken!</font> +<h2>Copyright</h2> +<p>Copyright © 2008 <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer +Gerhards</a> and +<a href="http://www.adiscon.com/en/">Adiscon</a>.</p> +<p> Permission is granted to copy, distribute and/or modify this +document under the terms of the GNU Free Documentation License, Version +1.2 or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license can be viewed at +<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</a>.</p> +</body></html> @@ -41,14 +41,12 @@ #include <fcntl.h> #endif #include <gssapi/gssapi.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "srUtils.h" #include "net.h" -#include "omfwd.h" #include "template.h" #include "msg.h" -#include "tcpsyslog.h" #include "module-template.h" #include "obj.h" #include "errmsg.h" @@ -68,13 +66,13 @@ static void display_status_(char *m, OM_uint32 code, int type) do { maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NO_OID, &msg_ctx, &msg); if (maj_stat != GSS_S_COMPLETE) { - errmsg.LogError(NO_ERRCODE, "GSS-API error in gss_display_status called from <%s>\n", m); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error in gss_display_status called from <%s>\n", m); break; } else { char buf[1024]; snprintf(buf, sizeof(buf), "GSS-API error %s: %s\n", m, (char *) msg.value); buf[sizeof(buf)/sizeof(char) - 1] = '\0'; - errmsg.LogError(NO_ERRCODE, "%s", buf); + errmsg.LogError(0, NO_ERRCODE, "%s", buf); } if (msg.length != 0) gss_release_buffer(&min_stat, &msg); @@ -164,12 +162,12 @@ static int recv_token(int s, gss_buffer_t tok) ret = read_all(s, (char *) lenbuf, 4); if (ret < 0) { - errmsg.LogError(NO_ERRCODE, "GSS-API error reading token length"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error reading token length"); return -1; } else if (!ret) { return 0; } else if (ret != 4) { - errmsg.LogError(NO_ERRCODE, "GSS-API error reading token length"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error reading token length"); return -1; } @@ -181,17 +179,17 @@ static int recv_token(int s, gss_buffer_t tok) tok->value = (char *) malloc(tok->length ? tok->length : 1); if (tok->length && tok->value == NULL) { - errmsg.LogError(NO_ERRCODE, "Out of memory allocating token data\n"); + errmsg.LogError(0, NO_ERRCODE, "Out of memory allocating token data\n"); return -1; } ret = read_all(s, (char *) tok->value, tok->length); if (ret < 0) { - errmsg.LogError(NO_ERRCODE, "GSS-API error reading token data"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error reading token data"); free(tok->value); return -1; } else if (ret != (int) tok->length) { - errmsg.LogError(NO_ERRCODE, "GSS-API error reading token data"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error reading token data"); free(tok->value); return -1; } @@ -216,19 +214,19 @@ static int send_token(int s, gss_buffer_t tok) ret = write_all(s, (char *) lenbuf, 4); if (ret < 0) { - errmsg.LogError(NO_ERRCODE, "GSS-API error sending token length"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error sending token length"); return -1; } else if (ret != 4) { - errmsg.LogError(NO_ERRCODE, "GSS-API error sending token length"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error sending token length"); return -1; } ret = write_all(s, tok->value, tok->length); if (ret < 0) { - errmsg.LogError(NO_ERRCODE, "GSS-API error sending token data"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error sending token data"); return -1; } else if (ret != (int) tok->length) { - errmsg.LogError(NO_ERRCODE, "GSS-API error sending token data"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API error sending token data"); return -1; } diff --git a/omfwd.c b/omfwd.c deleted file mode 100644 index 1f4d4430..00000000 --- a/omfwd.c +++ /dev/null @@ -1,645 +0,0 @@ -/* omfwd.c - * This is the implementation of the build-in forwarding output module. - * - * NOTE: read comments in module-template.h to understand how this file - * works! - * - * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c) - * This file is under development and has not yet arrived at being fully - * self-contained and a real object. So far, it is mostly an excerpt - * of the "old" message code without any modifications. However, it - * helps to have things at the right place one we go to the meat of it. - * - * Copyright 2007 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" -#ifdef SYSLOG_INET -#include "rsyslog.h" -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <netinet/in.h> -#include <netdb.h> -#include <fnmatch.h> -#include <assert.h> -#include <errno.h> -#include <ctype.h> -#include <unistd.h> -#ifdef USE_NETZIP -#include <zlib.h> -#endif -#include <pthread.h> -#include "syslogd.h" -#include "syslogd-types.h" -#include "srUtils.h" -#include "net.h" -#include "omfwd.h" -#include "template.h" -#include "msg.h" -#include "tcpsyslog.h" -#include "tcpclt.h" -#include "cfsysline.h" -#include "module-template.h" -#include "errmsg.h" - -MODULE_TYPE_OUTPUT - -/* internal structures - */ -DEF_OMOD_STATIC_DATA -DEFobjCurrIf(errmsg) -DEFobjCurrIf(net) -DEFobjCurrIf(tcpclt) - -typedef struct _instanceData { - 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; - struct addrinfo *f_addr; - int compressionLevel; /* 0 - no compression, else level for zlib */ - char *port; - int protocol; -# 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 */ -} instanceData; - -/* config data */ -static uchar *pszTplName = NULL; /* name of the default template to use */ - - -/* get the syslog forward port from selector_t. The passed in - * struct must be one that is setup for forwarding. - * rgerhards, 2007-06-28 - * 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) -{ - assert(pData != NULL); - if(pData->port == NULL) - return("514"); - else - return(pData->port); -} - -BEGINcreateInstance -CODESTARTcreateInstance - pData->sock = -1; -ENDcreateInstance - - -BEGINisCompatibleWithFeature -CODESTARTisCompatibleWithFeature - if(eFeat == sFEATURERepeatedMsgReduction) - iRet = RS_RET_OK; -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; - } - - /* final cleanup */ - if(pData->sock >= 0) - close(pData->sock); - if(pData->pSockArray != NULL) - net.closeUDPListenSockets(pData->pSockArray); - - if(pData->protocol == FORW_TCP) { - tcpclt.Destruct(&pData->pTCPClt); - } - - if(pData->f_hname != NULL) - free(pData->f_hname); - -ENDfreeInstance - - -BEGINdbgPrintInstInfo -CODESTARTdbgPrintInstInfo - printf("%s", pData->f_hname); -ENDdbgPrintInstInfo - - -/* Send a message via UDP - * rgehards, 2007-12-20 - */ -static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len) -{ - DEFiRet; - struct addrinfo *r; - int i; - unsigned lsent = 0; - int bSendSuccess; - - if(pData->pSockArray != NULL) { - /* we need to track if we have success sending to the remote - * peer. Success is indicated by at least one sendto() call - * 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 - */ - bSendSuccess = FALSE; - for (r = pData->f_addr; r; r = r->ai_next) { - for (i = 0; i < *pData->pSockArray; i++) { - lsent = sendto(pData->pSockArray[i+1], msg, len, 0, r->ai_addr, r->ai_addrlen); - if (lsent == len) { - bSendSuccess = TRUE; - break; - } else { - int eno = errno; - char errStr[1024]; - dbgprintf("sendto() error: %d = %s.\n", - eno, rs_strerror_r(eno, errStr, sizeof(errStr))); - } - } - if (lsent == len && !send_to_all) - break; - } - /* finished looping */ - if (bSendSuccess == FALSE) { - dbgprintf("error forwarding via udp, suspending\n"); - iRet = RS_RET_SUSPENDED; - } - } - - RETiRet; -} - -/* CODE FOR SENDING TCP MESSAGES */ - - -/* Send a frame via plain TCP protocol - * rgerhards, 2007-12-28 - */ -static rsRetVal TCPSendFrame(void *pvData, char *msg, size_t len) -{ - DEFiRet; - ssize_t lenSend; - instanceData *pData = (instanceData *) pvData; - - lenSend = send(pData->sock, msg, len, 0); - 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) { - /* no real error, could "just" not send everything... - * For the time being, we ignore this... - * rgerhards, 2005-10-25 - */ - dbgprintf("message not completely (tcp)send, ignoring %ld\n", (long) lenSend); - usleep(1000); /* experimental - might be benefitial in this situation */ - /* TODO: we need to revisit this code -- rgerhards, 2007-12-28 */ - } - - RETiRet; -} - - -/* This function is called immediately before a send retry is attempted. - * It shall clean up whatever makes sense. - * rgerhards, 2007-12-28 - */ -static rsRetVal TCPSendPrepRetry(void *pvData) -{ - DEFiRet; - instanceData *pData = (instanceData *) pvData; - - assert(pData != NULL); - close(pData->sock); - pData->sock = -1; - RETiRet; -} - - -/* initialies everything so that TCPSend can work. - * rgerhards, 2007-12-28 - */ -static rsRetVal TCPSendInit(void *pvData) -{ - DEFiRet; - 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; - } - - RETiRet; -} - - -/* try to resume connection if it is not ready - * rgerhards, 2007-08-02 - */ -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); - 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. - */ - hints.ai_flags = AI_NUMERICSERV; - hints.ai_family = family; - 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; - } - 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; - } - - RETiRet; -} - - -BEGINtryResume -CODESTARTtryResume - iRet = doTryResume(pData); -ENDtryResume - -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; - } -# 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; - } - } - break; - } -finalize_it: -ENDdoAction - - -BEGINparseSelectorAct - uchar *q; - int i; - int error; - int bErr; - struct addrinfo hints, *res; - 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; - } - /* 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? */ - ++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 - */ - if(*p == '[') { /* everything is hostname upto ']' */ - ++p; /* skip '[' */ - for(q = p ; *p && *p != ']' ; ++p) - /* JUST SKIP */; - if(*p == ']') { - *p = '\0'; /* trick to obtain hostname (later)! */ - ++p; /* eat it */ - } - } else { /* traditional view of hostname */ - for(q = p ; *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 != ';' && *p != '#' && !isspace((int) *p)) - ++p; /*JUST SKIP*/ - - /* TODO: make this if go away! */ - if(*p == ';' || *p == '#' || isspace(*p)) { - uchar cTmp = *p; - *p = '\0'; /* trick to obtain hostname (later)! */ - CHKmalloc(pData->f_hname = strdup((char*) q)); - *p = cTmp; - } else { - CHKmalloc(pData->f_hname = strdup((char*) q)); - } - - /* process template */ - CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, - (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName)); - - /* 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 = family; - 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)); - } - - } else { - iRet = RS_RET_CONFLINE_UNPROCESSED; - } - - /* 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 - - -BEGINmodExit -CODESTARTmodExit - /* release what we no longer need */ - objRelease(errmsg, CORE_COMPONENT); - objRelease(net, LM_NET_FILENAME); - objRelease(tcpclt, LM_TCPCLT_FILENAME); - - if(pszTplName != NULL) { - free(pszTplName); - pszTplName = NULL; - } -ENDmodExit - - -BEGINqueryEtryPt -CODESTARTqueryEtryPt -CODEqueryEtryPt_STD_OMOD_QUERIES -ENDqueryEtryPt - - -/* Reset config variables for this module to default values. - * rgerhards, 2008-03-28 - */ -static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) -{ - if(pszTplName != NULL) { - free(pszTplName); - pszTplName = NULL; - } - - return RS_RET_OK; -} - - -BEGINmodInit(Fwd) -CODESTARTmodInit - *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ -CODEmodInit_QueryRegCFSLineHdlr - CHKiRet(objUse(errmsg, CORE_COMPONENT)); - CHKiRet(objUse(net, LM_NET_FILENAME)); - CHKiRet(objUse(tcpclt, LM_TCPCLT_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/outchannel.c b/outchannel.c index d013ea08..5c348b63 100644 --- a/outchannel.c +++ b/outchannel.c @@ -37,7 +37,7 @@ #include <assert.h> #include "stringbuf.h" #include "outchannel.h" -#include "syslogd.h" +#include "dirty.h" static struct outchannel *ochRoot = NULL; /* the root of the outchannel list */ static struct outchannel *ochLast = NULL; /* points to the last element of the outchannel list */ @@ -3,7 +3,7 @@ * * begun 2005-09-15 rgerhards * - * Copyright 2005 + * Copyright 2005-2008 * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. * * This file is part of rsyslog. @@ -101,24 +101,9 @@ int parsIsAtEndOfParseString(rsParsObj *pThis); int parsGetCurrentPosition(rsParsObj *pThis); char parsPeekAtCharAtParsPtr(rsParsObj *pThis); #ifdef SYSLOG_INET -rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits); -#endif - -#if 0 /* later! - but leave it in in case we need it some day... */ -/* Parse a property - * This is a complex parsing routine. It parses an property - * entry suitable for use in the property replacer. It is currently - * just an idea if this should be a parser function. - */ -parsRet parsProp(parseObj *pThis, ?? **pPropEtry); +rsRetVal parsAddrWithBits(rsParsObj *pThis, netAddr_t **pIP, int *pBits); #endif #endif -/* - * Local variables: - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - * vi:set ai: +/* vim:set ai: */ diff --git a/plugins/im3195/Makefile.am b/plugins/im3195/Makefile.am new file mode 100644 index 00000000..bfceb71e --- /dev/null +++ b/plugins/im3195/Makefile.am @@ -0,0 +1,8 @@ +pkglib_LTLIBRARIES = im3195.la + +im3195_la_SOURCES = im3195.c +im3195_la_CPPFLAGS = $(rsrt_cflags) $(pthreads_cflags) $(LIBLOGGING_CFLAGS) +im3195_la_LDFLAGS = -module -avoid-version +im3195_la_LIBADD = $(LIBLOGGING_LIBS) + +EXTRA_DIST = diff --git a/plugins/im3195/im3195.c b/plugins/im3195/im3195.c new file mode 100644 index 00000000..32dd8dc1 --- /dev/null +++ b/plugins/im3195/im3195.c @@ -0,0 +1,167 @@ +/** + * The rfc3195 input module. + * + * Please note that this file replaces the rfc3195d daemon that was + * also present in pre-v3 versions of rsyslog. + * + * WARNING: due to no demand at all for RFC3195, we have converted rfc3195d + * to this input module, but we have NOT conducted any testing. Also, + * the module does not yet properly handle the recovery case. If someone + * intends to put this module into production, good testing should be + * made and it also is a good idea to notify me that you intend to use + * it in production. In this case, I'll probably give the module another + * cleanup. I don't do this now because so far it looks just like a big + * waste of time. -- rgerhards, 2008-04-16 + * + * \author Rainer Gerhards <rgerhards@adiscon.com> + * + * Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" + +#include <stdio.h> +#include <unistd.h> +#include <sys/errno.h> +#include <assert.h> +#include "rsyslog.h" +#include "dirty.h" +#include "liblogging/liblogging.h" +#include "liblogging/srAPI.h" +#include "liblogging/syslogmessage.h" +#include "module-template.h" +#include "cfsysline.h" +#include "errmsg.h" + +MODULE_TYPE_INPUT + +/* Module static data */ +DEF_IMOD_STATIC_DATA +DEFobjCurrIf(errmsg) + +/* configuration settings */ +static int listenPort = 601; + +/* we use a global API object below, because this listener is + * not very complex. As such, this hack should not harm anything. + * rgerhards, 2005-10-12 + */ +static srAPIObj* pAPI; + + +/* This method is called when a message has been fully received. + * It passes the received message to the rsyslog main message + * queue. Please note that this callback is synchronous, thus + * liblogging will be on hold until it returns. This is important + * to note because in an error case we might stay in this code + * for an extended amount of time. So far, we think this is the + * best solution, but real-world experience might tell us a + * different truth ;) + */ +void OnReceive(srAPIObj __attribute__((unused)) *pMyAPI, srSLMGObj* pSLMG) +{ + uchar *pszRawMsg; + uchar *fromHost = (uchar*) "[unset]"; /* TODO: get hostname */ + uchar *fromHostIP = (uchar*) "[unset]"; /* TODO: get hostname */ + + srSLMGGetRawMSG(pSLMG, &pszRawMsg); + + parseAndSubmitMessage(fromHost, fromHostIP, pszRawMsg, strlen((char*)pszRawMsg), + MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_FULL_DELAY); +} + + +BEGINrunInput +CODESTARTrunInput + /* this is an endless loop - it is terminated when the thread is + * signalled to do so. This, however, is handled by the framework, + * right into the sleep below. + */ + while(!pThrd->bShallStop) { + /* now move the listener to running state. Control will only + * return after SIGUSR1. + */ + if((iRet = srAPIRunListener(pAPI)) != SR_RET_OK) { + errmsg.LogError(0, NO_ERRCODE, "error %d running liblogging listener - im3195 is defunct", iRet); + FINALIZE; /* this causes im3195 to become defunct; TODO: recovery handling */ + } + } +finalize_it: +ENDrunInput + + +BEGINwillRun +CODESTARTwillRun + if((pAPI = srAPIInitLib()) == NULL) { + errmsg.LogError(0, NO_ERRCODE, "error initializing liblogging - im3195 is defunct"); + ABORT_FINALIZE(RS_RET_ERR); + } + + if((iRet = srAPISetOption(pAPI, srOPTION_BEEP_LISTENPORT, listenPort)) != SR_RET_OK) { + errmsg.LogError(0, NO_ERRCODE, "error %d setting liblogging listen port - im3195 is defunct", iRet); + FINALIZE; + } + + if((iRet = srAPISetupListener(pAPI, OnReceive)) != SR_RET_OK) { + errmsg.LogError(0, NO_ERRCODE, "error %d setting up liblogging listener - im3195 is defunct", iRet); + FINALIZE; + } + +finalize_it: +ENDwillRun + + +BEGINafterRun +CODESTARTafterRun + dbgprintf("Shutting down rfc3195d. Be patient, this can take up to 30 seconds...\n"); + srAPIShutdownListener(pAPI); +ENDafterRun + + +BEGINmodExit +CODESTARTmodExit + srAPIExitLib(pAPI); /* terminate liblogging */ + /* release objects we used */ + objRelease(errmsg, CORE_COMPONENT); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_IMOD_QUERIES +ENDqueryEtryPt + +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + listenPort = 601; + return RS_RET_OK; +} + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + CHKiRet(omsdRegCFSLineHdlr((uchar *)"input3195listenport", 0, eCmdHdlrInt, NULL, &listenPort, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit +/* vim:set ai: + */ diff --git a/plugins/imfile/Makefile.am b/plugins/imfile/Makefile.am index 23b64d1b..a4011d12 100644 --- a/plugins/imfile/Makefile.am +++ b/plugins/imfile/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imfile.la imfile_la_SOURCES = imfile.c -imfile_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imfile_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imfile_la_LDFLAGS = -module -avoid-version imfile_la_LIBADD = diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c index 2df1aaf7..3bc07b9c 100644 --- a/plugins/imfile/imfile.c +++ b/plugins/imfile/imfile.c @@ -36,13 +36,14 @@ # include <sys/stat.h> #endif #include "rsyslog.h" /* error codes etc... */ -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" /* access to config file objects */ #include "module-template.h" /* generic module interface code - very important, read it! */ #include "srUtils.h" /* some utility functions */ #include "msg.h" #include "stream.h" #include "errmsg.h" +#include "glbl.h" #include "datetime.h" MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ @@ -52,6 +53,7 @@ MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ /* Module static data */ DEF_IMOD_STATIC_DATA /* must be present, starts static data */ DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) DEFobjCurrIf(datetime) typedef struct fileInfo_s { @@ -95,7 +97,7 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine) MsgSetUxTradMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine)); MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine)); MsgSetMSG(pMsg, (char*)rsCStrGetSzStr(cstrLine)); - MsgSetHOSTNAME(pMsg, (char*)LocalHostName); + MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName()); MsgSetTAG(pMsg, (char*)pInfo->pszTag); pMsg->iFacility = LOG_FAC(pInfo->iFacility); pMsg->iSeverity = LOG_PRI(pInfo->iSeverity); @@ -121,7 +123,7 @@ openFile(fileInfo_t *pThis) /* Construct file name */ lenSFNam = snprintf((char*)pszSFNam, sizeof(pszSFNam) / sizeof(uchar), "%s/%s", - (char*) glblGetWorkDir(), (char*)pThis->pszStateFile); + (char*) glbl.GetWorkDir(), (char*)pThis->pszStateFile); /* check if the file exists */ if(stat((char*) pszSFNam, &stat_buf) == -1) { @@ -179,7 +181,10 @@ static void pollFileCancelCleanup(void *pArg) rsCStrDestruct(ppCStr); ENDfunc; } + + /* poll a file, need to check file rollover etc. open file if not open */ +#pragma GCC diagnostic ignored "-Wempty-body" static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData) { cstr_t *pCStr = NULL; @@ -220,6 +225,7 @@ finalize_it: RETiRet; } +#pragma GCC diagnostic warning "-Wempty-body" /* This function is the cancel cleanup handler. It is called when rsyslog decides the @@ -276,6 +282,7 @@ inputModuleCleanup(void __attribute__((unused)) *arg) * On spamming the main queue: keep in mind that it will automatically rate-limit * ourselfes if we begin to overrun it. So we really do not need to care here. */ +#pragma GCC diagnostic ignored "-Wempty-body" BEGINrunInput int i; int bHadFileData; /* were there at least one file with data during this run? */ @@ -308,6 +315,7 @@ CODESTARTrunInput pthread_cleanup_pop(0); /* just for completeness, but never called... */ RETiRet; /* use it to make sure the housekeeping is done! */ ENDrunInput +#pragma GCC diagnostic warning "-Wempty-body" /* END no-touch zone * * ------------------------------------------------------------------------------------------ */ @@ -322,7 +330,7 @@ ENDrunInput BEGINwillRun CODESTARTwillRun if(iFilPtr == 0) { - errmsg.LogError(NO_ERRCODE, "No files configured to be monitored"); + errmsg.LogError(0, RS_RET_NO_RUN, "No files configured to be monitored"); ABORT_FINALIZE(RS_RET_NO_RUN); } @@ -346,7 +354,7 @@ persistStrmState(fileInfo_t *pInfo) /* TODO: create a function persistObj in obj.c? */ CHKiRet(strmConstruct(&psSF)); - CHKiRet(strmSetDir(psSF, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(psSF, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_WRITE)); CHKiRet(strmSetiAddtlOpenFlags(psSF, O_TRUNC)); CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE)); @@ -393,6 +401,7 @@ BEGINmodExit CODESTARTmodExit /* release objects we used */ objRelease(datetime, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -450,21 +459,21 @@ static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal) pThis = &files[iFilPtr]; /* TODO: check for strdup() NULL return */ if(pszFileName == NULL) { - errmsg.LogError(NO_ERRCODE, "imfile error: no file name given, file monitor can not be created"); + errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile error: no file name given, file monitor can not be created"); ABORT_FINALIZE(RS_RET_CONFIG_ERROR); } else { pThis->pszFileName = (uchar*) strdup((char*) pszFileName); } if(pszFileTag == NULL) { - errmsg.LogError(NO_ERRCODE, "imfile error: no tag value given , file monitor can not be created"); + errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile error: no tag value given , file monitor can not be created"); ABORT_FINALIZE(RS_RET_CONFIG_ERROR); } else { pThis->pszTag = (uchar*) strdup((char*) pszFileTag); } if(pszStateFile == NULL) { - errmsg.LogError(NO_ERRCODE, "imfile error: not state file name given, file monitor can not be created"); + errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile error: not state file name given, file monitor can not be created"); ABORT_FINALIZE(RS_RET_CONFIG_ERROR); } else { pThis->pszStateFile = (uchar*) strdup((char*) pszStateFile); @@ -473,7 +482,7 @@ static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal) pThis->iSeverity = iSeverity; pThis->iFacility = iFacility; } else { - errmsg.LogError(NO_ERRCODE, "Too many file monitors configured - ignoring this one"); + errmsg.LogError(0, RS_RET_OUT_OF_DESRIPTORS, "Too many file monitors configured - ignoring this one"); ABORT_FINALIZE(RS_RET_OUT_OF_DESRIPTORS); } @@ -500,6 +509,7 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilename", 0, eCmdHdlrGetWord, diff --git a/plugins/imgssapi/.cvsignore b/plugins/imgssapi/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/imgssapi/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/imgssapi/Makefile.am b/plugins/imgssapi/Makefile.am index 42a243f4..a5cce320 100644 --- a/plugins/imgssapi/Makefile.am +++ b/plugins/imgssapi/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imgssapi.la imgssapi_la_SOURCES = imgssapi.c -imgssapi_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imgssapi_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imgssapi_la_LDFLAGS = -module -avoid-version imgssapi_la_LIBADD = $(gss_libs) diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c index 74d5d5c5..766cb519 100644 --- a/plugins/imgssapi/imgssapi.c +++ b/plugins/imgssapi/imgssapi.c @@ -45,7 +45,7 @@ #endif #include <gssapi/gssapi.h> #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "module-template.h" #include "net.h" @@ -54,6 +54,7 @@ #include "tcpsrv.h" #include "tcps_sess.h" #include "errmsg.h" +#include "netstrm.h" MODULE_TYPE_INPUT @@ -67,7 +68,7 @@ MODULE_TYPE_INPUT static rsRetVal addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal); static int TCPSessGSSInit(void); static void TCPSessGSSClose(tcps_sess_t* pSess); -static int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len); +static rsRetVal TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len, ssize_t *); static rsRetVal onSessAccept(tcpsrv_t *pThis, tcps_sess_t *ppSess); static rsRetVal OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *ppSess); @@ -77,6 +78,7 @@ DEFobjCurrIf(tcpsrv) DEFobjCurrIf(tcps_sess) DEFobjCurrIf(gssutil) DEFobjCurrIf(errmsg) +DEFobjCurrIf(netstrm) DEFobjCurrIf(net) static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */ @@ -241,11 +243,12 @@ onErrClose(tcps_sess_t *pSess) /* open the listen sockets */ -static int* +static rsRetVal doOpenLstnSocks(tcpsrv_t *pSrv) { int *pRet = NULL; gsssrv_t *pGSrv; + DEFiRet; ISOBJ_TYPE_assert(pSrv, tcpsrv); pGSrv = pSrv->pUsr; @@ -255,39 +258,44 @@ doOpenLstnSocks(tcpsrv_t *pSrv) if(pGSrv->allowedMethods) { if(pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) { if(TCPSessGSSInit()) { - errmsg.LogError(NO_ERRCODE, "GSS-API initialization failed\n"); + errmsg.LogError(0, NO_ERRCODE, "GSS-API initialization failed\n"); pGSrv->allowedMethods &= ~(ALLOWEDMETHOD_GSS); } } if(pGSrv->allowedMethods) { /* fallback to plain TCP */ - if((pRet = tcpsrv.create_tcp_socket(pSrv)) != NULL) { - dbgprintf("Opened %d syslog TCP port(s).\n", *pRet); - } + CHKiRet(tcpsrv.create_tcp_socket(pSrv)); + dbgprintf("Opened %d syslog TCP port(s).\n", *pRet); } } - return pRet; +finalize_it: + RETiRet; } -static int -doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf) +static rsRetVal +doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd) { - int state; + DEFiRet; int allowedMethods; gss_sess_t *pGSess; assert(pSess != NULL); assert(pSess->pUsr != NULL); pGSess = (gss_sess_t*) pSess->pUsr; + assert(piLenRcvd != NULL); allowedMethods = pGSess->allowedMethods; - if(allowedMethods & ALLOWEDMETHOD_GSS) - state = TCPSessGSSRecv(pSess, buf, lenBuf); - else - state = recv(pSess->sock, buf, lenBuf, 0); - return state; + if(allowedMethods & ALLOWEDMETHOD_GSS) { + CHKiRet(TCPSessGSSRecv(pSess, buf, lenBuf, piLenRcvd)); + } else { + *piLenRcvd = lenBuf; + CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd) != RS_RET_OK); + } + +finalize_it: + RETiRet; } @@ -391,7 +399,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) dbgprintf("GSS-API Trying to accept TCP session %p\n", pSess); - fdSess = pSess->sock; // TODO: method access! + CHKiRet(netstrm.GetSock(pSess->pStrm, &fdSess)); // TODO: method access! if (allowedMethods & ALLOWEDMETHOD_TCP) { int len; fd_set fds; @@ -405,7 +413,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) ret = select(fdSess + 1, &fds, NULL, NULL, &tv); } while (ret < 0 && errno == EINTR); if (ret < 0) { - errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); + errmsg.LogError(0, RS_RET_ERR, "TCP session %p will be closed, error ignored\n", pSess); ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes } else if (ret == 0) { dbgprintf("GSS-API Reverting to plain TCP\n"); @@ -420,7 +428,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) if (ret == 0) dbgprintf("GSS-API Connection closed by peer\n"); else - errmsg.LogError(NO_ERRCODE, "TCP(GSS) session %p will be closed, error ignored\n", pSess); + errmsg.LogError(0, RS_RET_ERR, "TCP(GSS) session %p will be closed, error ignored\n", pSess); ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes } @@ -440,7 +448,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) if (ret == 0) dbgprintf("GSS-API Connection closed by peer\n"); else - errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); + errmsg.LogError(0, NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes } } @@ -462,7 +470,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) sess_flags = &pGSess->gss_flags; do { if (gssutil.recv_token(fdSess, &recv_tok) <= 0) { - errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); + errmsg.LogError(0, NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes } maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context, gss_server_creds, @@ -481,7 +489,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) dbgprintf("GSS-API Reverting to plain TCP\n"); dbgprintf("tcp session socket with new data: #%d\n", fdSess); if(tcps_sess.DataRcvd(pSess, buf, ret) != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "Tearing down TCP Session %p - see " + errmsg.LogError(0, NO_ERRCODE, "Tearing down TCP Session %p - see " "previous messages for reason(s)\n", pSess); ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes } @@ -494,7 +502,7 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess) if (send_tok.length != 0) { if(gssutil.send_token(fdSess, &send_tok) < 0) { gss_release_buffer(&min_stat, &send_tok); - errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); + errmsg.LogError(0, NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess); if (*context != GSS_C_NO_CONTEXT) gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER); ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes @@ -521,25 +529,26 @@ finalize_it: } -/* returns: number of bytes read or -1 on error - * Replaces recv() for gssapi connections. +/* Replaces recv() for gssapi connections. */ -int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len) +int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len, ssize_t *piLenRcvd) { + DEFiRet; gss_buffer_desc xmit_buf, msg_buf; gss_ctx_id_t *context; OM_uint32 maj_stat, min_stat; int fdSess; int conf_state; - int state, len; + int state; gss_sess_t *pGSess; assert(pSess->pUsr != NULL); + assert(piLenRcvd != NULL); pGSess = (gss_sess_t*) pSess->pUsr; - fdSess = pSess->sock; + netstrm.GetSock(pSess->pStrm, &fdSess); // TODO: method access, CHKiRet! if ((state = gssutil.recv_token(fdSess, &xmit_buf)) <= 0) - return state; + ABORT_FINALIZE(RS_RET_GSS_ERR); context = &pGSess->gss_context; maj_stat = gss_unwrap(&min_stat, *context, &xmit_buf, &msg_buf, @@ -550,18 +559,19 @@ int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len) free(xmit_buf.value); xmit_buf.value = 0; } - return (-1); + ABORT_FINALIZE(RS_RET_GSS_ERR); } if (xmit_buf.value) { free(xmit_buf.value); xmit_buf.value = 0; } - len = msg_buf.length < buf_len ? msg_buf.length : buf_len; - memcpy(buf, msg_buf.value, len); + *piLenRcvd = msg_buf.length < buf_len ? msg_buf.length : buf_len; + memcpy(buf, msg_buf.value, *piLenRcvd); gss_release_buffer(&min_stat, &msg_buf); - return len; +finalize_it: + RETiRet; } @@ -638,6 +648,7 @@ CODESTARTmodExit objRelease(tcpsrv, LM_TCPSRV_FILENAME); objRelease(gssutil, LM_GSSUTIL_FILENAME); objRelease(errmsg, CORE_COMPONENT); + objRelease(netstrm, LM_NETSTRM_FILENAME); objRelease(net, LM_NET_FILENAME); ENDmodExit @@ -684,6 +695,7 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME)); CHKiRet(objUse(gssutil, LM_GSSUTIL_FILENAME)); CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME)); CHKiRet(objUse(net, LM_NET_FILENAME)); /* register config file handlers */ diff --git a/plugins/imklog/.cvsignore b/plugins/imklog/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/imklog/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/imklog/Makefile.am b/plugins/imklog/Makefile.am index 246b3306..8f50cfb2 100644 --- a/plugins/imklog/Makefile.am +++ b/plugins/imklog/Makefile.am @@ -11,6 +11,6 @@ if ENABLE_IMKLOG_LINUX imklog_la_SOURCES += linux.c module.h ksym.c ksyms.h ksym_mod.c endif -imklog_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imklog_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imklog_la_LDFLAGS = -module -avoid-version imklog_la_LIBADD = diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c index f7aee5b1..1fbc2874 100644 --- a/plugins/imklog/imklog.c +++ b/plugins/imklog/imklog.c @@ -45,21 +45,23 @@ #include <stdarg.h> #include <ctype.h> -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "obj.h" #include "msg.h" #include "module-template.h" #include "datetime.h" #include "imklog.h" +#include "glbl.h" MODULE_TYPE_INPUT /* Module static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(datetime) +DEFobjCurrIf(glbl) -/* configuration settings TODO: move to instance data? */ +/* configuration settings */ int dbgPrintSymbols = 0; /* this one is extern so the helpers can access it! */ int symbols_twice = 0; int use_syscall = 0; @@ -95,7 +97,9 @@ enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity) MsgSetUxTradMsg(pMsg, (char*)msg); MsgSetRawMsg(pMsg, (char*)msg); MsgSetMSG(pMsg, (char*)msg); - MsgSetHOSTNAME(pMsg, (char*)LocalHostName); + MsgSetRcvFrom(pMsg, (char*)glbl.GetLocalHostName()); + MsgSetRcvFromIP(pMsg, (uchar*)"127.0.0.1"); + MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName()); MsgSetTAG(pMsg, (char*)pszTag); pMsg->iFacility = LOG_FAC(iFacility); pMsg->iSeverity = LOG_PRI(iSeverity); @@ -226,6 +230,7 @@ ENDafterRun BEGINmodExit CODESTARTmodExit /* release objects we used */ + objRelease(glbl, CORE_COMPONENT); objRelease(datetime, CORE_COMPONENT); ENDmodExit @@ -252,6 +257,7 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(datetime, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); iFacilIntMsg = klogFacilIntMsg(); diff --git a/plugins/imklog/imklog.h b/plugins/imklog/imklog.h index a37ecc9e..0847140b 100644 --- a/plugins/imklog/imklog.h +++ b/plugins/imklog/imklog.h @@ -28,7 +28,7 @@ #define IMKLOG_H_INCLUDED 1 #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" /* interface to "drivers" * the platform specific drivers must implement these entry points. Only one diff --git a/plugins/imklog/linux.c b/plugins/imklog/linux.c index faf20134..198b7c0e 100644 --- a/plugins/imklog/linux.c +++ b/plugins/imklog/linux.c @@ -32,7 +32,6 @@ #include <signal.h> #include <string.h> #include <pthread.h> -#include "syslogd.h" #include "cfsysline.h" #include "template.h" #include "msg.h" @@ -147,9 +146,7 @@ static enum LOGSRC GetKernelLogSrc(void) if ( (kmsg = open(_PATH_KLOG, O_RDONLY)) < 0 ) { - char sz[512]; - snprintf(sz, sizeof(sz), "imklog: Cannot open proc file system, %d - %s.\n", errno, strerror(errno)); - logmsgInternal(LOG_SYSLOG|LOG_ERR, sz, ADDDATE); + imklogLogIntMsg(LOG_ERR, "imklog: Cannot open proc file system, %d.\n", errno); ksyslog(7, NULL, 0); /* TODO: check this, implement more */ return(none); } @@ -428,11 +425,9 @@ static void LogKernelLine(void) memset(log_buffer, '\0', sizeof(log_buffer)); if ( (rdcnt = ksyslog(2, log_buffer, sizeof(log_buffer)-1)) < 0 ) { - char sz[512]; if(errno == EINTR) return; - snprintf(sz, sizeof(sz), "imklog: Error return from sys_sycall: %d - %s\n", errno, strerror(errno)); - logmsgInternal(LOG_SYSLOG|LOG_ERR, sz, ADDDATE); + imklogLogIntMsg(LOG_ERR, "imklog Error return from sys_sycall: %d\n", errno); } else LogLine(log_buffer, rdcnt); diff --git a/plugins/immark/.cvsignore b/plugins/immark/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/immark/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/immark/Makefile.am b/plugins/immark/Makefile.am index 3dc0e408..9c0f8f64 100644 --- a/plugins/immark/Makefile.am +++ b/plugins/immark/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = immark.la immark_la_SOURCES = immark.c immark.h -immark_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +immark_la_CPPFLAGS = $(rsrt_cflags) -I$(top_srcdir) $(pthreads_cflags) immark_la_LDFLAGS = -module -avoid-version immark_la_LIBADD = diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c index 0bc31995..bdca4d58 100644 --- a/plugins/immark/immark.c +++ b/plugins/immark/immark.c @@ -37,9 +37,10 @@ #include <signal.h> #include <string.h> #include <pthread.h> -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "module-template.h" +#include "errmsg.h" MODULE_TYPE_INPUT @@ -75,7 +76,7 @@ CODESTARTrunInput * rgerhards, 2007-12-17 */ CHKiRet(thrdSleep(pThrd, iMarkMessagePeriod, 0)); /* seconds, micro seconds */ - logmsgInternal(LOG_INFO, "-- MARK --", ADDDATE|MARK); + logmsgInternal(NO_ERRCODE, LOG_INFO, (uchar*)"-- MARK --", ADDDATE|MARK); } finalize_it: return iRet; diff --git a/plugins/imrelp/.cvsignore b/plugins/imrelp/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/imrelp/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/imrelp/Makefile.am b/plugins/imrelp/Makefile.am index 53c9322c..a96e2b42 100644 --- a/plugins/imrelp/Makefile.am +++ b/plugins/imrelp/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imrelp.la imrelp_la_SOURCES = imrelp.c -imrelp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(RELP_CFLAGS) +imrelp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(RELP_CFLAGS) $(rsrt_cflags) imrelp_la_LDFLAGS = -module -avoid-version imrelp_la_LIBADD = $(RELP_LIBS) diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c index b7308016..5c9bbce1 100644 --- a/plugins/imrelp/imrelp.c +++ b/plugins/imrelp/imrelp.c @@ -38,7 +38,7 @@ #include <sys/socket.h> #include <librelp.h> #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "module-template.h" #include "net.h" @@ -76,12 +76,14 @@ isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((u * are different from our rsRetVal. So we can simply use our own iRet system * to fulfill the requirement. * rgerhards, 2008-03-21 + * TODO: we currently do not receive the remote hosts's IP. As a work-around, we + * use "???" for the time being. -- rgerhards, 2008-05-16 */ static relpRetVal onSyslogRcv(uchar *pHostname, uchar __attribute__((unused)) *pIP, uchar *pMsg, size_t lenMsg) { DEFiRet; - parseAndSubmitMessage((char*)pHostname, (char*)pMsg, lenMsg, MSG_PARSE_HOSTNAME, + parseAndSubmitMessage(pHostname, (uchar*) "[unset]", pMsg, lenMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); RETiRet; diff --git a/plugins/imtcp/.cvsignore b/plugins/imtcp/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/imtcp/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/imtcp/Makefile.am b/plugins/imtcp/Makefile.am index fe43cd98..de746a95 100644 --- a/plugins/imtcp/Makefile.am +++ b/plugins/imtcp/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imtcp.la imtcp_la_SOURCES = imtcp.c -imtcp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imtcp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imtcp_la_LDFLAGS = -module -avoid-version imtcp_la_LIBADD = diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c index 7baa95f2..f01a9f0f 100644 --- a/plugins/imtcp/imtcp.c +++ b/plugins/imtcp/imtcp.c @@ -23,6 +23,20 @@ * A copy of the GPL can be found in the file "COPYING" in this distribution. */ +/* This note shall explain the calling sequence while we do not have + * have full RainerScript support for (TLS) sender authentication: + * + * imtcp --> tcpsrv --> netstrms (this sequence stored pPermPeers in netstrms class) + * then a callback (doOpenLstnSocks) into imtcp happens, which in turn calls + * into tcpsrv.create_tcp_socket(), + * which calls into netstrm.LstnInit(), which receives a pointer to netstrms obj + * which calls into the driver function LstnInit (again, netstrms obj passed) + * which finally calls back into netstrms obj's get functions to obtain the auth + * parameters and then applies them to the driver object instance + * + * rgerhards, 2008-05-19 + */ + #include "config.h" #include <stdlib.h> #include <assert.h> @@ -39,11 +53,14 @@ #include <fcntl.h> #endif #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "module-template.h" #include "net.h" +#include "netstrm.h" +#include "errmsg.h" #include "tcpsrv.h" +#include "net.h" /* for permittedPeers, may be removed when this is removed */ MODULE_TYPE_INPUT @@ -52,12 +69,18 @@ DEF_IMOD_STATIC_DATA DEFobjCurrIf(tcpsrv) DEFobjCurrIf(tcps_sess) DEFobjCurrIf(net) +DEFobjCurrIf(netstrm) +DEFobjCurrIf(errmsg) /* Module static data */ static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */ +static permittedPeers_t *pPermPeersRoot = NULL; + /* config settings */ static int iTCPSessMax = 200; /* max number of sessions */ +static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ +static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ /* callbacks */ @@ -70,7 +93,7 @@ isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((u } -static int* +static rsRetVal doOpenLstnSocks(tcpsrv_t *pSrv) { ISOBJ_TYPE_assert(pSrv, tcpsrv); @@ -78,14 +101,17 @@ doOpenLstnSocks(tcpsrv_t *pSrv) } -static int -doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf) +static rsRetVal +doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd) { - int state; + DEFiRet; assert(pSess != NULL); + assert(piLenRcvd != NULL); - state = recv(pSess->sock, buf, lenBuf, 0); - return state; + *piLenRcvd = lenBuf; + CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd)); +finalize_it: + RETiRet; } static rsRetVal @@ -115,9 +141,23 @@ onErrClose(tcps_sess_t *pSess) /* ------------------------------ end callbacks ------------------------------ */ +/* 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; +} + + static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal) { DEFiRet; + if(pOurTcpsrv == NULL) { CHKiRet(tcpsrv.Construct(&pOurTcpsrv)); CHKiRet(tcpsrv.SetCBIsPermittedHost(pOurTcpsrv, isPermittedHost)); @@ -125,11 +165,25 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa CHKiRet(tcpsrv.SetCBOpenLstnSocks(pOurTcpsrv, doOpenLstnSocks)); CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose)); CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose)); + CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode)); + /* now set optional params, but only if they were actually configured */ + if(pszStrmDrvrAuthMode != NULL) { + CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode)); + } + if(pPermPeersRoot != NULL) { + CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot)); + } + /* most params set, now start listener */ tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal); CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv)); } finalize_it: + if(iRet != RS_RET_OK) { + errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet); + if(pOurTcpsrv != NULL) + tcpsrv.Destruct(&pOurTcpsrv); + } RETiRet; } @@ -170,10 +224,16 @@ CODESTARTmodExit if(pOurTcpsrv != NULL) iRet = tcpsrv.Destruct(&pOurTcpsrv); + if(pPermPeersRoot != NULL) { + net.DestructPermittedPeers(&pPermPeersRoot); + } + /* release objects we used */ objRelease(net, LM_NET_FILENAME); + objRelease(netstrm, LM_NETSTRMS_FILENAME); objRelease(tcps_sess, LM_TCPSRV_FILENAME); objRelease(tcpsrv, LM_TCPSRV_FILENAME); + objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -181,6 +241,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { iTCPSessMax = 200; + iStrmDrvrMode = 0; return RS_RET_OK; } @@ -199,14 +260,22 @@ CODEmodInit_QueryRegCFSLineHdlr pOurTcpsrv = NULL; /* request objects we use */ CHKiRet(objUse(net, LM_NET_FILENAME)); + CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME)); CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME)); CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord, addTCPListener, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpmaxsessions", 0, eCmdHdlrInt, NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdrivermode", 0, + eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverauthmode", 0, + eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverpermittedpeer", 0, + eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit diff --git a/plugins/imtemplate/Makefile.am b/plugins/imtemplate/Makefile.am index a9221817..0ea4355e 100644 --- a/plugins/imtemplate/Makefile.am +++ b/plugins/imtemplate/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imtemplate.la imtemplate_la_SOURCES = imtemplate.c -imtemplate_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imtemplate_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imtemplate_la_LDFLAGS = -module -avoid-version imtemplate_la_LIBADD = diff --git a/plugins/imudp/.cvsignore b/plugins/imudp/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/imudp/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/imudp/Makefile.am b/plugins/imudp/Makefile.am index 53fdad16..28ee9853 100644 --- a/plugins/imudp/Makefile.am +++ b/plugins/imudp/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imudp.la imudp_la_SOURCES = imudp.c -imudp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imudp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imudp_la_LDFLAGS = -module -avoid-version imudp_la_LIBADD = diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c index 81e7d7a4..f8beb01f 100644 --- a/plugins/imudp/imudp.c +++ b/plugins/imudp/imudp.c @@ -33,12 +33,13 @@ #include <unistd.h> #include <netdb.h> #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" #include "net.h" #include "cfsysline.h" #include "module-template.h" #include "srUtils.h" #include "errmsg.h" +#include "glbl.h" MODULE_TYPE_INPUT @@ -47,6 +48,7 @@ MODULE_TYPE_INPUT /* Module static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) DEFobjCurrIf(net) static int *udpLstnSocks = NULL; /* Internet datagram sockets, first element is nbr of elements @@ -133,6 +135,7 @@ BEGINrunInput struct sockaddr_storage frominet; socklen_t socklen; uchar fromHost[NI_MAXHOST]; + uchar fromHostIP[NI_MAXHOST]; uchar fromHostFQDN[NI_MAXHOST]; ssize_t l; CODESTARTrunInput @@ -180,7 +183,7 @@ CODESTARTrunInput l = recvfrom(udpLstnSocks[i+1], (char*) pRcvBuf, MAXLINE - 1, 0, (struct sockaddr *)&frominet, &socklen); if (l > 0) { - if(net.cvthname(&frominet, fromHost, fromHostFQDN) == RS_RET_OK) { + if(net.cvthname(&frominet, fromHost, fromHostFQDN, fromHostIP) == RS_RET_OK) { dbgprintf("Message from inetd socket: #%d, host: %s\n", udpLstnSocks[i+1], fromHost); /* Here we check if a host is permitted to send us @@ -191,12 +194,12 @@ CODESTARTrunInput */ if(net.isAllowedSender(net.pAllowedSenders_UDP, (struct sockaddr *)&frominet, (char*)fromHostFQDN)) { - parseAndSubmitMessage((char*)fromHost, (char*) pRcvBuf, l, + parseAndSubmitMessage(fromHost, fromHostIP, pRcvBuf, l, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_NO_DELAY); } else { dbgprintf("%s is not an allowed sender\n", (char*)fromHostFQDN); - if(option_DisallowWarning) { - errmsg.LogError(NO_ERRCODE, "UDP message from disallowed sender %s discarded", + if(glbl.GetOption_DisallowWarning) { + errmsg.LogError(0, NO_ERRCODE, "UDP message from disallowed sender %s discarded", (char*)fromHost); } } @@ -205,7 +208,7 @@ CODESTARTrunInput char errStr[1024]; rs_strerror_r(errno, errStr, sizeof(errStr)); dbgprintf("INET socket error: %d = %s.\n", errno, errStr); - errmsg.LogError(NO_ERRCODE, "recvfrom inet"); + errmsg.LogError(errno, NO_ERRCODE, "recvfrom inet"); /* should be harmless */ sleep(1); } @@ -257,6 +260,7 @@ BEGINmodExit CODESTARTmodExit /* release what we no longer need */ objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); objRelease(net, LM_NET_FILENAME); ENDmodExit @@ -285,6 +289,7 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); /* register config file handlers */ diff --git a/plugins/imuxsock/.cvsignore b/plugins/imuxsock/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/imuxsock/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/imuxsock/Makefile.am b/plugins/imuxsock/Makefile.am index e165bb7d..11a0ba3a 100644 --- a/plugins/imuxsock/Makefile.am +++ b/plugins/imuxsock/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = imuxsock.la imuxsock_la_SOURCES = imuxsock.c -imuxsock_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +imuxsock_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) imuxsock_la_LDFLAGS = -module -avoid-version imuxsock_la_LIBADD = diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 60ccaffb..05bcb642 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -35,11 +35,13 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/un.h> -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "module-template.h" #include "srUtils.h" #include "errmsg.h" +#include "net.h" +#include "glbl.h" MODULE_TYPE_INPUT @@ -62,6 +64,7 @@ MODULE_TYPE_INPUT /* Module static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) static int startIndexUxLocalSockets; /* process funix from that index on (used to * suppress local logging. rgerhards 2005-08-01 @@ -100,8 +103,6 @@ static rsRetVal setSystemLogTimestampIgnore(void __attribute__((unused)) *pVal, */ static rsRetVal addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNewVal) { - char errStr[1024]; - if(nfunix < MAXFUNIX) { if(*pNewVal == ':') { funixParseHost[nfunix] = 1; @@ -113,9 +114,8 @@ static rsRetVal addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNe funixn[nfunix++] = pNewVal; } else { - snprintf(errStr, sizeof(errStr), "rsyslogd: Out of unix socket name descriptors, ignoring %s\n", + errmsg.LogError(0, NO_ERRCODE, "Out of unix socket name descriptors, ignoring %s\n", pNewVal); - logmsgInternal(LOG_SYSLOG|LOG_ERR, errStr, ADDDATE); } return RS_RET_OK; @@ -159,7 +159,7 @@ static int create_unix_socket(const char *path) SUN_LEN(&sunx)) < 0 || chmod(path, 0666) < 0) { snprintf(line, sizeof(line), "cannot create %s", path); - errmsg.LogError(NO_ERRCODE, "%s", line); + errmsg.LogError(errno, NO_ERRCODE, "%s", line); dbgprintf("cannot create %s (%d).\n", path, errno); close(fd); return -1; @@ -176,17 +176,18 @@ static rsRetVal readSocket(int fd, int bParseHost, int flags) { DEFiRet; int iRcvd; - char line[MAXLINE +1]; + uchar line[MAXLINE +1]; iRcvd = recv(fd, line, MAXLINE - 1, 0); dbgprintf("Message from UNIX socket: #%d\n", fd); if (iRcvd > 0) { - parseAndSubmitMessage((char*)LocalHostName, line, iRcvd, bParseHost, flags, eFLOWCTL_NO_DELAY); + parseAndSubmitMessage(glbl.GetLocalHostName(), (uchar*)"127.0.0.1", line, + iRcvd, bParseHost, flags, eFLOWCTL_NO_DELAY); } else if (iRcvd < 0 && errno != EINTR) { char errStr[1024]; rs_strerror_r(errno, errStr, sizeof(errStr)); dbgprintf("UNIX socket error: %d = %s.\n", errno, errStr); - errmsg.LogError(NO_ERRCODE, "recvfrom UNIX"); + errmsg.LogError(errno, NO_ERRCODE, "recvfrom UNIX"); } RETiRet; @@ -289,6 +290,8 @@ ENDafterRun BEGINmodExit CODESTARTmodExit + objRelease(glbl, CORE_COMPONENT); + objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -319,6 +322,7 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); /* initialize funixn[] array */ for(i = 1 ; i < MAXFUNIX ; ++i) { @@ -346,6 +350,5 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketignoremsgtimestamp", 0, eCmdHdlrBinary, setSystemLogTimestampIgnore, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit -/* - * vi:set ai: +/* vim:set ai: */ diff --git a/plugins/omgssapi/.cvsignore b/plugins/omgssapi/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/omgssapi/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/omgssapi/Makefile.am b/plugins/omgssapi/Makefile.am index 5280a1ce..c2cbe387 100644 --- a/plugins/omgssapi/Makefile.am +++ b/plugins/omgssapi/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = omgssapi.la omgssapi_la_SOURCES = omgssapi.c -omgssapi_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +omgssapi_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) omgssapi_la_LDFLAGS = -module -avoid-version omgssapi_la_LIBADD = $(gss_libs) diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c index 34abfe0a..82fca2db 100644 --- a/plugins/omgssapi/omgssapi.c +++ b/plugins/omgssapi/omgssapi.c @@ -43,38 +43,27 @@ #endif #include <pthread.h> #include <gssapi/gssapi.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "srUtils.h" #include "net.h" -#include "omfwd.h" #include "template.h" #include "msg.h" -#include "tcpsyslog.h" #include "cfsysline.h" #include "module-template.h" #include "gss-misc.h" #include "tcpclt.h" +#include "glbl.h" #include "errmsg.h" 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 */ DEF_OMOD_STATIC_DATA DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) DEFobjCurrIf(gssutil) DEFobjCurrIf(tcpclt) @@ -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); @@ -285,7 +268,7 @@ finalize_it: RETiRet; fail: - errmsg.LogError(NO_ERRCODE, "GSS-API Context initialization failed\n"); + errmsg.LogError(0, RS_RET_GSS_SENDINIT_ERROR, "GSS-API Context initialization failed\n"); gss_release_name(&min_stat, &target_name); gss_release_buffer(&min_stat, &out_tok); if (*context != GSS_C_NO_CONTEXT) { @@ -365,13 +348,12 @@ static rsRetVal doTryResume(instanceData *pData) * a common function. */ hints.ai_flags = AI_NUMERICSERV; - hints.ai_family = family; + hints.ai_family = glbl.GetDefPFFamily(); hints.ai_socktype = 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->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) @@ -520,12 +501,12 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) ++p; /* eat */ pData->compressionLevel = iLevel; } else { - errmsg.LogError(NO_ERRCODE, "Invalid compression level '%c' specified in " + errmsg.LogError(0, 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 " + errmsg.LogError(0, 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? */ @@ -533,7 +514,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* 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); + errmsg.LogError(0, 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 @@ -549,7 +530,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* 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 gssapi forward action."); + errmsg.LogError(0, NO_ERRCODE, "Option block not terminated in gssapi forward 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 @@ -567,7 +548,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* 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, " + errmsg.LogError(0, 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(). @@ -581,8 +562,17 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* now skip to template */ bErr = 0; - while(*p && *p != ';' && *p != '#' && !isspace((int) *p)) - ++p; /*JUST SKIP*/ + while(*p && *p != ';') { + if(*p && *p != ';' && !isspace((int) *p)) { + if(bErr == 0) { /* only 1 error msg! */ + bErr = 1; + errno = 0; + errmsg.LogError(0, NO_ERRCODE, "invalid selector line (port), probably not doing " + "what was intended"); + } + } + ++p; + } /* TODO: make this if go away! */ if(*p == ';' || *p == '#' || isspace(*p)) { @@ -602,12 +592,10 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) memset(&hints, 0, sizeof(hints)); /* port must be numeric, because config file syntax requests this */ hints.ai_flags = AI_NUMERICSERV; - hints.ai_family = family; + hints.ai_family = glbl.GetDefPFFamily(); 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; @@ -630,6 +618,7 @@ ENDparseSelectorAct BEGINmodExit CODESTARTmodExit + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); objRelease(gssutil, LM_GSSUTIL_FILENAME); objRelease(tcpclt, LM_TCPCLT_FILENAME); @@ -659,7 +648,7 @@ static rsRetVal setGSSMode(void __attribute__((unused)) *pVal, uchar *mode) gss_mode = GSSMODE_ENC; dbgprintf("GSS-API gssmode set to GSSMODE_ENC\n"); } else { - errmsg.LogError(NO_ERRCODE, "unknown gssmode parameter: %s", (char *) mode); + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown gssmode parameter: %s", (char *) mode); iRet = RS_RET_INVALID_PARAMS; } free(mode); @@ -688,6 +677,7 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(gssutil, LM_GSSUTIL_FILENAME)); CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME)); diff --git a/plugins/omlibdbi/.cvsignore b/plugins/omlibdbi/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/omlibdbi/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/omlibdbi/Makefile.am b/plugins/omlibdbi/Makefile.am index 872fc67c..d224f9e4 100644 --- a/plugins/omlibdbi/Makefile.am +++ b/plugins/omlibdbi/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = omlibdbi.la omlibdbi_la_SOURCES = omlibdbi.c -omlibdbi_la_CPPFLAGS = -I$(top_srcdir) $(libdbi_cflags) $(pthreads_cflags) +omlibdbi_la_CPPFLAGS = -I$(top_srcdir) $(libdbi_cflags) $(pthreads_cflags) $(rsrt_cflags) omlibdbi_la_LDFLAGS = -module -avoid-version omlibdbi_la_LIBADD = $(libdbi_libs) diff --git a/plugins/omlibdbi/omlibdbi.c b/plugins/omlibdbi/omlibdbi.c index a942a453..6f130f54 100644 --- a/plugins/omlibdbi/omlibdbi.c +++ b/plugins/omlibdbi/omlibdbi.c @@ -40,7 +40,7 @@ #include <errno.h> #include <time.h> #include <dbi/dbi.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "cfsysline.h" #include "srUtils.h" @@ -133,7 +133,7 @@ reportDBError(instanceData *pData, int bSilent) /* output log message */ errno = 0; if(pData->conn == NULL) { - errmsg.LogError(NO_ERRCODE, "unknown DB error occured - could not obtain connection handle"); + errmsg.LogError(0, NO_ERRCODE, "unknown DB error occured - could not obtain connection handle"); } else { /* we can ask dbi for the error description... */ uDBErrno = dbi_conn_error(pData->conn, &pszDbiErr); snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", uDBErrno, pszDbiErr); @@ -141,7 +141,7 @@ reportDBError(instanceData *pData, int bSilent) dbgprintf("libdbi, DBError(silent): %s\n", errMsg); else { pData->uLastDBErrno = uDBErrno; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, NO_ERRCODE, "%s", errMsg); } } @@ -167,10 +167,10 @@ static rsRetVal initConn(instanceData *pData, int bSilent) iDrvrsLoaded = dbi_initialize((char*) dbiDrvrDir); # endif if(iDrvrsLoaded == 0) { - errmsg.LogError(NO_ERRCODE, "libdbi error: libdbi or libdbi drivers not present on this system - suspending."); + errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi or libdbi drivers not present on this system - suspending."); ABORT_FINALIZE(RS_RET_SUSPENDED); } else if(iDrvrsLoaded < 0) { - errmsg.LogError(NO_ERRCODE, "libdbi error: libdbi could not be initialized - suspending."); + errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi could not be initialized - suspending."); ABORT_FINALIZE(RS_RET_SUSPENDED); } bDbiInitialized = 1; /* we are done for the rest of our existence... */ @@ -182,7 +182,7 @@ static rsRetVal initConn(instanceData *pData, int bSilent) pData->conn = dbi_conn_new((char*)pData->drvrName); # endif if(pData->conn == NULL) { - errmsg.LogError(NO_ERRCODE, "can not initialize libdbi connection"); + errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize libdbi connection"); iRet = RS_RET_SUSPENDED; } else { /* we could get the handle, now on with work... */ /* Connect to database */ @@ -272,7 +272,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* no create the instance based on what we currently have */ if(drvrName == NULL) { - errmsg.LogError(NO_ERRCODE, "omlibdbi: no db driver name given - action can not be created"); + errmsg.LogError(0, RS_RET_NO_DRIVERNAME, "omlibdbi: no db driver name given - action can not be created"); ABORT_FINALIZE(RS_RET_NO_DRIVERNAME); } diff --git a/plugins/ommail/Makefile.am b/plugins/ommail/Makefile.am index 7e9f5f13..fa470a43 100644 --- a/plugins/ommail/Makefile.am +++ b/plugins/ommail/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = ommail.la ommail_la_SOURCES = ommail.c -ommail_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +ommail_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) ommail_la_LDFLAGS = -module -avoid-version ommail_la_LIBADD = diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c index 218c73c9..4bbb844a 100644 --- a/plugins/ommail/ommail.c +++ b/plugins/ommail/ommail.c @@ -44,12 +44,13 @@ #include <netdb.h> #include <time.h> #include <sys/socket.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "srUtils.h" #include "cfsysline.h" #include "module-template.h" #include "errmsg.h" +#include "glbl.h" MODULE_TYPE_OUTPUT @@ -57,6 +58,7 @@ MODULE_TYPE_OUTPUT */ DEF_OMOD_STATIC_DATA DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) static uchar *pszSrv = NULL; static uchar *pszSrvPort = NULL; @@ -415,7 +417,7 @@ sendSMTP(instanceData *pData, uchar *body, uchar *subject) CHKiRet(readResponse(pData, &iState, 220)); CHKiRet(Send(pData->md.smtp.sock, "HELO ", 5)); - CHKiRet(Send(pData->md.smtp.sock, (char*)LocalHostName, strlen((char*)LocalHostName))); + CHKiRet(Send(pData->md.smtp.sock, (char*)glbl.GetLocalHostName(), strlen((char*)glbl.GetLocalHostName()))); CHKiRet(Send(pData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1)); CHKiRet(readResponse(pData, &iState, 250)); @@ -526,11 +528,11 @@ CODESTARTparseSelectorAct /* TODO: check strdup() result */ if(pszFrom == NULL) { - errmsg.LogError(NO_ERRCODE, "no sender address given - specify $ActionMailFrom"); + errmsg.LogError(0, RS_RET_MAIL_NO_FROM, "no sender address given - specify $ActionMailFrom"); ABORT_FINALIZE(RS_RET_MAIL_NO_FROM); } if(pszTo == NULL) { - errmsg.LogError(NO_ERRCODE, "no recipient address given - specify $ActionMailTo"); + errmsg.LogError(0, RS_RET_MAIL_NO_TO, "no recipient address given - specify $ActionMailTo"); ABORT_FINALIZE(RS_RET_MAIL_NO_TO); } @@ -589,6 +591,7 @@ CODESTARTmodExit freeConfigVariables(); /* release what we no longer need */ + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -616,6 +619,7 @@ CODESTARTmodInit CODEmodInit_QueryRegCFSLineHdlr /* tell which objects we need */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpserver", 0, eCmdHdlrGetWord, NULL, &pszSrv, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpport", 0, eCmdHdlrGetWord, NULL, &pszSrvPort, STD_LOADABLE_MODULE_ID)); diff --git a/plugins/ommysql/.cvsignore b/plugins/ommysql/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/ommysql/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/ommysql/Makefile.am b/plugins/ommysql/Makefile.am index 3b4e6d75..d5433a40 100644 --- a/plugins/ommysql/Makefile.am +++ b/plugins/ommysql/Makefile.am @@ -1,7 +1,7 @@ pkglib_LTLIBRARIES = ommysql.la ommysql_la_SOURCES = ommysql.c ommysql.h -ommysql_la_CPPFLAGS = -I$(top_srcdir) $(mysql_cflags) $(pthreads_cflags) +ommysql_la_CPPFLAGS = $(rsrt_cflags) $(mysql_cflags) $(pthreads_cflags) ommysql_la_LDFLAGS = -module -avoid-version ommysql_la_LIBADD = $(mysql_libs) diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c index 807351d2..22abb1d2 100644 --- a/plugins/ommysql/ommysql.c +++ b/plugins/ommysql/ommysql.c @@ -37,7 +37,7 @@ #include <time.h> #include <mysql/mysql.h> #include <mysql/errmsg.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "srUtils.h" #include "template.h" @@ -120,7 +120,7 @@ static void reportDBError(instanceData *pData, int bSilent) /* output log message */ errno = 0; if(pData->f_hmysql == NULL) { - errmsg.LogError(NO_ERRCODE, "unknown DB error occured - could not obtain MySQL handle"); + errmsg.LogError(0, NO_ERRCODE, "unknown DB error occured - could not obtain MySQL handle"); } else { /* we can ask mysql for the error description... */ uMySQLErrno = mysql_errno(pData->f_hmysql); snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", uMySQLErrno, @@ -129,7 +129,7 @@ static void reportDBError(instanceData *pData, int bSilent) dbgprintf("mysql, DBError(silent): %s\n", errMsg); else { pData->uLastMySQLErrno = uMySQLErrno; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, NO_ERRCODE, "%s", errMsg); } } @@ -150,7 +150,7 @@ static rsRetVal initMySQL(instanceData *pData, int bSilent) pData->f_hmysql = mysql_init(NULL); if(pData->f_hmysql == NULL) { - errmsg.LogError(NO_ERRCODE, "can not initialize MySQL handle"); + errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MySQL handle"); iRet = RS_RET_SUSPENDED; } else { /* we could get the handle, now on with work... */ /* Connect to database */ @@ -275,7 +275,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) * Retries make no sense. */ if (iMySQLPropErr) { - errmsg.LogError(NO_ERRCODE, "Trouble with MySQL connection properties. -MySQL logging disabled"); + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Trouble with MySQL connection properties. -MySQL logging disabled"); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } else { pData->f_dbsrvPort = (unsigned) iSrvPort; /* set configured port */ diff --git a/plugins/ompgsql/.cvsignore b/plugins/ompgsql/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/ompgsql/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/ompgsql/Makefile.am b/plugins/ompgsql/Makefile.am index b2e3effa..cc1c5f49 100644 --- a/plugins/ompgsql/Makefile.am +++ b/plugins/ompgsql/Makefile.am @@ -1,7 +1,7 @@ pkglib_LTLIBRARIES = ompgsql.la ompgsql_la_SOURCES = ompgsql.c ompgsql.h -ompgsql_la_CPPFLAGS = -I$(top_srcdir) $(pgsql_cflags) +ompgsql_la_CPPFLAGS = -I$(top_srcdir) $(pgsql_cflags) $(rsrt_cflags) ompgsql_la_LDFLAGS = -module -avoid-version ompgsql_la_LIBADD = $(pgsql_libs) diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c index 1d7b2eb7..f8685672 100644 --- a/plugins/ompgsql/ompgsql.c +++ b/plugins/ompgsql/ompgsql.c @@ -36,7 +36,7 @@ #include <errno.h> #include <time.h> #include <libpq-fe.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "srUtils.h" #include "template.h" @@ -113,7 +113,7 @@ static void reportDBError(instanceData *pData, int bSilent) /* output log message */ errno = 0; if(pData->f_hpgsql == NULL) { - errmsg.LogError(NO_ERRCODE, "unknown DB error occured - could not obtain PgSQL handle"); + errmsg.LogError(0, NO_ERRCODE, "unknown DB error occured - could not obtain PgSQL handle"); } else { /* we can ask pgsql for the error description... */ ePgSQLStatus = PQstatus(pData->f_hpgsql); snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", ePgSQLStatus, @@ -122,7 +122,7 @@ static void reportDBError(instanceData *pData, int bSilent) dbgprintf("pgsql, DBError(silent): %s\n", errMsg); else { pData->eLastPgSQLStatus = ePgSQLStatus; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, NO_ERRCODE, "%s", errMsg); } } @@ -264,7 +264,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) * Retries make no sense. */ if (iPgSQLPropErr) { - errmsg.LogError(NO_ERRCODE, "Trouble with PgSQL connection properties. -PgSQL logging disabled"); + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Trouble with PgSQL connection properties. -PgSQL logging disabled"); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } else { CHKiRet(initPgSQL(pData, 0)); diff --git a/plugins/omrelp/.cvsignore b/plugins/omrelp/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/omrelp/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/omrelp/Makefile.am b/plugins/omrelp/Makefile.am index dfc2111f..f8384f42 100644 --- a/plugins/omrelp/Makefile.am +++ b/plugins/omrelp/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = omrelp.la omrelp_la_SOURCES = omrelp.c -omrelp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(RELP_CFLAGS) +omrelp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(RELP_CFLAGS) $(rsrt_cflags) omrelp_la_LDFLAGS = -module -avoid-version omrelp_la_LIBADD = $(RELP_LIBS) diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index 04571682..71d6e797 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -36,11 +36,12 @@ #include <errno.h> #include <ctype.h> #include <librelp.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "srUtils.h" #include "cfsysline.h" #include "module-template.h" +#include "glbl.h" #include "errmsg.h" MODULE_TYPE_OUTPUT @@ -49,6 +50,7 @@ MODULE_TYPE_OUTPUT */ DEF_OMOD_STATIC_DATA DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) static relpEngine_t *pRelpEngine; /* our relp engine */ @@ -118,7 +120,7 @@ static rsRetVal doConnect(instanceData *pData) DEFiRet; if(pData->bInitialConnect) { - iRet = relpCltConnect(pData->pRelpClt, family, (uchar*) pData->port, (uchar*) pData->f_hname); + iRet = relpCltConnect(pData->pRelpClt, glbl.GetDefPFFamily(), (uchar*) pData->port, (uchar*) pData->f_hname); if(iRet == RELP_RET_OK) pData->bInitialConnect = 0; } else { @@ -162,7 +164,6 @@ CODESTARTdoAction /* forward */ ret = relpCltSendSyslog(pData->pRelpClt, (uchar*) pMsg, lenMsg); -RUNLOG_VAR("%d", ret); if(ret != RELP_RET_OK) { /* error! */ dbgprintf("error forwarding via relp, suspending\n"); @@ -218,16 +219,16 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) ++p; /* eat */ pData->compressionLevel = iLevel; } else { - errmsg.LogError(NO_ERRCODE, "Invalid compression level '%c' specified in " + errmsg.LogError(0, 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 " + errmsg.LogError(0, NO_ERRCODE, "Compression requested, but rsyslogd is not compiled " "with compression support - request ignored."); # endif /* #ifdef USE_NETZIP */ } else { /* invalid option! Just skip it... */ - errmsg.LogError(NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p); + errmsg.LogError(0, 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 @@ -243,7 +244,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* 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."); + errmsg.LogError(0, 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 @@ -261,7 +262,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* SKIP AND COUNT */; pData->port = malloc(i + 1); if(pData->port == NULL) { - errmsg.LogError(NO_ERRCODE, "Could not get memory to store relp port, " + errmsg.LogError(0, NO_ERRCODE, "Could not get memory to store relp 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 getRelpPt() */ } else { @@ -277,7 +278,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) if(bErr == 0) { /* only 1 error msg! */ bErr = 1; errno = 0; - errmsg.LogError(NO_ERRCODE, "invalid selector line (port), probably not doing " + errmsg.LogError(0, NO_ERRCODE, "invalid selector line (port), probably not doing " "what was intended"); } } @@ -311,6 +312,7 @@ CODESTARTmodExit relpEngineDestruct(&pRelpEngine); /* release what we no longer need */ + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -332,6 +334,7 @@ CODEmodInit_QueryRegCFSLineHdlr /* tell which objects we need */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); ENDmodInit /* vim:set ai: diff --git a/plugins/omsnmp/.cvsignore b/plugins/omsnmp/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/omsnmp/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/omsnmp/Makefile.am b/plugins/omsnmp/Makefile.am index d74f7bb4..d784faca 100644 --- a/plugins/omsnmp/Makefile.am +++ b/plugins/omsnmp/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = omsnmp.la omsnmp_la_SOURCES = omsnmp.c omsnmp.h -omsnmp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +omsnmp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) omsnmp_la_LDFLAGS = -module -avoid-version omsnmp_la_LIBADD = $(snmp_libs) diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c index 22d48340..72fa8d64 100644 --- a/plugins/omsnmp/omsnmp.c +++ b/plugins/omsnmp/omsnmp.c @@ -36,7 +36,7 @@ #include <netdb.h> #include <ctype.h> #include <assert.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "cfsysline.h" #include "module-template.h" @@ -179,7 +179,7 @@ static rsRetVal omsnmp_initSession(instanceData *pData) pData->snmpsession = snmp_open(&session); if (pData->snmpsession == NULL) { - errmsg.LogError(NO_ERRCODE, "omsnmp_initSession: snmp_open to host '%s' on Port '%d' failed\n", pData->szTarget, pData->iPort); + errmsg.LogError(0, RS_RET_SUSPENDED, "omsnmp_initSession: snmp_open to host '%s' on Port '%d' failed\n", pData->szTarget, pData->iPort); /* Stay suspended */ iRet = RS_RET_SUSPENDED; } @@ -218,7 +218,7 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) if (!snmp_parse_oid( (char*) pData->szEnterpriseOID, enterpriseoid, &enterpriseoidlen )) { strErr = snmp_api_errstring(snmp_errno); - errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Parsing EnterpriseOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr); + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Parsing EnterpriseOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); } @@ -254,7 +254,7 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) if ( snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', (char*) pData->szSnmpTrapOID ) != 0) { strErr = snmp_api_errstring(snmp_errno); - errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Adding trap OID failed '%s' with error '%s' \n", pData->szSnmpTrapOID, strErr); + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Adding trap OID failed '%s' with error '%s' \n", pData->szSnmpTrapOID, strErr); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); } } @@ -269,14 +269,14 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) if (iErrCode) { const char *str = snmp_api_errstring(iErrCode); - errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Invalid SyslogMessage OID, error code '%d' - '%s'\n", iErrCode, str ); + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Invalid SyslogMessage OID, error code '%d' - '%s'\n", iErrCode, str ); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); } } else { strErr = snmp_api_errstring(snmp_errno); - errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Parsing SyslogMessageOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr); + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Parsing SyslogMessageOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); } @@ -287,7 +287,7 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) { /* Debug Output! */ int iErrorCode = pData->snmpsession->s_snmp_errno; - errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: snmp_send failed error '%d', Description='%s'\n", iErrorCode*(-1), api_errors[iErrorCode*(-1)]); + errmsg.LogError(0, RS_RET_SUSPENDED, "omsnmp_sendsnmp: snmp_send failed error '%d', Description='%s'\n", iErrorCode*(-1), api_errors[iErrorCode*(-1)]); /* Clear Session */ omsnmp_exitSession(pData); diff --git a/plugins/omtesting/.cvsignore b/plugins/omtesting/.cvsignore deleted file mode 100644 index 9730646f..00000000 --- a/plugins/omtesting/.cvsignore +++ /dev/null @@ -1,6 +0,0 @@ -.deps -.libs -Makefile -Makefile.in -*.la -*.lo diff --git a/plugins/omtesting/Makefile.am b/plugins/omtesting/Makefile.am index 7e376683..8e98ca63 100644 --- a/plugins/omtesting/Makefile.am +++ b/plugins/omtesting/Makefile.am @@ -1,6 +1,6 @@ pkglib_LTLIBRARIES = omtesting.la omtesting_la_SOURCES = omtesting.c -omtesting_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +omtesting_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) omtesting_la_LDFLAGS = -module -avoid-version omtesting_la_LIBADD = diff --git a/plugins/omtesting/omtesting.c b/plugins/omtesting/omtesting.c index 15d3cb80..411bcf88 100644 --- a/plugins/omtesting/omtesting.c +++ b/plugins/omtesting/omtesting.c @@ -49,7 +49,7 @@ #include <string.h> #include <ctype.h> #include <assert.h> -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "module-template.h" diff --git a/redhat/rsyslog b/redhat/rsyslog deleted file mode 100644 index ee9be79b..00000000 --- a/redhat/rsyslog +++ /dev/null @@ -1,12 +0,0 @@ -# Options to syslogd -# -m 0 disables 'MARK' messages. -# -r enables logging from remote machines -# -x disables DNS lookups on messages recieved with -r -# See syslogd(8) for more details -SYSLOGD_OPTIONS="-m 0" -# Options to klogd -# -2 prints all kernel oops messages twice; once for klogd to decode, and -# once for processing with 'ksymoops' -# -x disables all klogd processing of oops messages entirely -# See klogd(8) for more details -KLOGD_OPTIONS="-x" diff --git a/redhat/rsyslog.conf b/redhat/rsyslog.conf deleted file mode 100644 index 9d34c805..00000000 --- a/redhat/rsyslog.conf +++ /dev/null @@ -1,26 +0,0 @@ -# Log all kernel messages to the console. -# Logging much else clutters up the screen. -#kern.* /dev/console - -# Log anything (except mail) of level info or higher. -# Don't log private authentication messages! -*.info;mail.none;authpriv.none;cron.none /var/log/messages - -# The authpriv file has restricted access. -authpriv.* /var/log/secure - -# Log all the mail messages in one place. -mail.* -/var/log/maillog - - -# Log cron stuff -cron.* /var/log/cron - -# Everybody gets emergency messages -*.emerg * - -# Save news errors of level crit and higher in a special file. -uucp,news.crit /var/log/spooler - -# Save boot messages also to boot.log -local7.* /var/log/boot.log diff --git a/redhat/rsyslog.init b/redhat/rsyslog.init deleted file mode 100644 index 6b6f0649..00000000 --- a/redhat/rsyslog.init +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash -# -# rsyslog Starts rsyslogd/rklogd. -# -# -# chkconfig: 2345 12 88 -# description: Syslog is the facility by which many daemons use to log \ -# messages to various system log files. It is a good idea to always \ -# run rsyslog. -### BEGIN INIT INFO -# Provides: $syslog -# Short-Description: Enhanced system logging and kernel message trapping daemons -# Description: Rsyslog is an enhanced multi-threaded syslogd supporting, -# among others, MySQL, syslog/tcp, RFC 3195, permitted -# sender lists, filtering on any message part, and fine -# grain output format control. -### END INIT INFO - -# Source function library. -. /etc/init.d/functions - -RETVAL=0 - -start() { - [ -x /sbin/rsyslogd ] || exit 5 - [ -x /sbin/rklogd ] || exit 5 - - # Source config - if [ -f /etc/sysconfig/rsyslog ] ; then - . /etc/sysconfig/rsyslog - else - SYSLOGD_OPTIONS="-m 0" - KLOGD_OPTIONS="-2" - fi - - umask 077 - - echo -n $"Starting system logger (rsyslog): " - daemon rsyslogd $SYSLOGD_OPTIONS - RETVAL=$? - echo - echo -n $"Starting kernel logger (rklogd): " - daemon rklogd $KLOGD_OPTIONS - echo - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/rsyslog - return $RETVAL -} -stop() { - echo -n $"Shutting down kernel logger (rklogd): " - killproc rklogd - echo - echo -n $"Shutting down system logger (rsyslog): " - killproc rsyslogd - RETVAL=$? - echo - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/rsyslog - return $RETVAL -} -rhstatus() { - status rsyslogd - status rklogd -} -restart() { - stop - start -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - status) - rhstatus - ;; - restart|reload) - restart - ;; - condrestart) - [ -f /var/lock/subsys/rsyslog ] && restart || : - ;; - *) - echo $"Usage: $0 {start|stop|status|restart|condrestart}" - exit 2 -esac - -exit $? diff --git a/redhat/rsyslog.log b/redhat/rsyslog.log deleted file mode 100644 index e0593a26..00000000 --- a/redhat/rsyslog.log +++ /dev/null @@ -1,6 +0,0 @@ -/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler /var/log/boot.log /var/log/cron { - sharedscripts - postrotate - /bin/kill -HUP `cat /var/run/rsyslogd.pid 2> /dev/null` 2> /dev/null || true - endscript -} diff --git a/redhat/rsyslog.sysconfig b/redhat/rsyslog.sysconfig deleted file mode 100644 index ee9be79b..00000000 --- a/redhat/rsyslog.sysconfig +++ /dev/null @@ -1,12 +0,0 @@ -# Options to syslogd -# -m 0 disables 'MARK' messages. -# -r enables logging from remote machines -# -x disables DNS lookups on messages recieved with -r -# See syslogd(8) for more details -SYSLOGD_OPTIONS="-m 0" -# Options to klogd -# -2 prints all kernel oops messages twice; once for klogd to decode, and -# once for processing with 'ksymoops' -# -x disables all klogd processing of oops messages entirely -# See klogd(8) for more details -KLOGD_OPTIONS="-x" diff --git a/rfc3195d.8 b/rfc3195d.8 deleted file mode 100644 index ae191df6..00000000 --- a/rfc3195d.8 +++ /dev/null @@ -1,84 +0,0 @@ -.\" Copyright 2005 Rainer Gerhards and Adiscon for the rsyslog modifications -.\" Distributed under the GNU General Public License. -.\" -.TH RFC3195D 8 "02 April 2008" "Version 3.14.0" "Linux System Administration" -.SH NAME -rfc3195d \- RFC 3195 listener -.SH SYNOPSIS -.B rfc3195d -.RB [ " \-d " ] -.RB [ " \-p" -.IB socket -] -.RB [ " \-r" -.IB port -] -.RB [ " \-v " ] -.LP -.SH DESCRIPTION -.B Rfc3195d -is a utility for receiving syslog messages via RFC 3195. Both -RAW and COOKED profiles are supported (but COOKED only without -relay-specific PATH elements). -rfc3195d accepts messages via RFC 3195 and forwards them to -the local domain socket specified in the -p option -(/dev/log3195 by default). There, the messages can be picked up -by the system syslogd. While rfc3195d can work with any syslogd, -we highly recommend using -.B rsyslogd, -because it has special handling -for the messages forwarded by rfc3195d. To enable message -reception in -.B rsyslogd, -use the "-a :/dev/log3195" command line -option (the colon in front of the socket name tells -.B rsyslogd -that the messages contain hostnames - this is vital to get the -right sender name into your logs). - -.B Rfc3195d -currently has very limited functionality. Most importantly, -it does not allow to limit the senders it receives messages from. -Documentation is also very sparse. The situation should improve over -time as the rsyslog project is continously being enhanced. -.LP -.SH OPTIONS -.TP -.BI "\-p " "socket" -The socket the received messages are to be sent to. If not specified, -/dev/log3195 is used. -.TP -.BI "\-r " "port" -The listen port to use. If not specified, IANA-assigned port 601 is used. -.TP -.B "\-d" -Turns on debug mode. In it, rfc3195d spits out diagnostic information -to stdout. -.TP -.B "\-v" -Print version and exit. -.SH SIGNALS -.B Rfc3195d -reacts to a set of signals. -.TP -.B SIGTERM -.B Rfc3195d -terminates. -.TP -.B SIGUSR1 -.B Rfc3195d -terminates. -.LP -.SH SEE ALSO -.BR rsyslog.conf (5), -.BR rsyslogd (8) -.LP -.SH MORE INFORMATION -Is available on the project home page at http://www.rsyslog.com -.LP -.SH COLLABORATORS -Rfc3195d uses liblogging (http://www.liblogging.org) for RFC 3195 -protocol handling. -.PD 0 -.TP -Rainer Gerhards <rgerhards@adiscon.com> diff --git a/rfc3195d.c b/rfc3195d.c deleted file mode 100644 index dd8497cb..00000000 --- a/rfc3195d.c +++ /dev/null @@ -1,289 +0,0 @@ -/** - * rfc3195d.c - * This is an RFC 3195 listener. All data received is forwarded to - * local UNIX domain socket, where it can be picked up by a - * syslog daemon (like rsyslogd ;)). - * - * \author Rainer Gerhards <rgerhards@adiscon.com> - * - * Copyright 2003-2005 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> -#ifndef FEATURE_RFC3195 -/* this is a trick: if RFC3195 is not to be supported, we just do an - * error message. - */ -int main() -{ - fprintf(stderr, "error: not compiled with FEATURE_RFC3195 - terminating.\n"); - return(1); -} -#else -#include <unistd.h> -#include <signal.h> -#include <sys/socket.h> -#include <sys/errno.h> -#include "rsyslog.h" -#include "liblogging/liblogging.h" -#include "liblogging/srAPI.h" -#include "liblogging/syslogmessage.h" - -/* configurable params! */ -static char* pPathLogname = "/dev/log3195"; -static char *PidFile; -static int NoFork = 0; -static int Debug = 0; -static int listenPort = 601; - -/* we use a global API object below, because this listener is - * not very complex. As such, this hack should not harm anything. - * rgerhards, 2005-10-12 - */ -static srAPIObj* pAPI; - -static int LogFile = -1; /* fd for log */ -static int connected; /* have done connect */ -static struct sockaddr SyslogAddr; /* AF_UNIX address of local logger */ - -/* small usage info */ -static int usage() -{ - /* The following usage line is what we intend to have - it - * is commented out as a reminder. The one below is what we - * currently actually do... - fprintf(stderr, "usage: rfc3195d [-dv] [-i pidfile] [-n] [-p path]\n"); - */ - fprintf(stderr, "usage: rfc3195d [-dv] [-r port] [-p path]\n"); - exit(1); -} - -/* CLOSELOG -- close the system log - */ -static void closelog(void) -{ - close(LogFile); - LogFile = -1; - connected = 0; -} - -/* OPENLOG -- open system log - */ -static void openlog() -{ - if (LogFile == -1) { - SyslogAddr.sa_family = AF_UNIX; - strncpy(SyslogAddr.sa_data, pPathLogname, - sizeof(SyslogAddr.sa_data)); - LogFile = socket(AF_UNIX, SOCK_DGRAM, 0); - if(LogFile < 0) { - char errStr[1024]; - printf("error opening '%s': %s\n", - pPathLogname, rs_strerror_r(errno, errStr, sizeof(errStr))); - } - } - if (LogFile != -1 && !connected && - connect(LogFile, &SyslogAddr, sizeof(SyslogAddr.sa_family)+ - strlen(SyslogAddr.sa_data)) != -1) - connected = 1; - else { - char errStr[1024]; - printf("error connecting '%s': %s\n", - pPathLogname, rs_strerror_r(errno, errStr, sizeof(errStr))); - } -} - - -/* This method is called when a message has been fully received. - * It passes the received message to the specified unix domain - * socket. Please note that this callback is synchronous, thus - * liblogging will be on hold until it returns. This is important - * to note because in an error case we might stay in this code - * for an extended amount of time. So far, we think this is the - * best solution, but real-world experience might tell us a - * different truth ;) - * rgerhards 2005-10-12 - */ -void OnReceive(srAPIObj* pAPI, srSLMGObj* pSLMG) -{ - unsigned char *pszRawMsg; - int iRetries; /* number of retries connecting to log socket */ - int iSleep; - int iWriteOffset; - ssize_t nToWrite; - ssize_t nWritten; - - srSLMGGetRawMSG(pSLMG, &pszRawMsg); - - /* we need to loop writing the message. At least in - * theory, a single write might not send all data to the - * syslogd. So we need to continue until everything is written. - * Also, we need to check if there are any socket erros, in - * which case we reconect. We will re-try indefinitely, if this - * is not acceptable, you need to change the code. - * rgerhards 2005-10-12 - */ - iRetries = 0; - nToWrite = strlen(pszRawMsg); - iWriteOffset = 0; - while(nToWrite != 0) { - if(LogFile < 0 || !connected) - openlog(); - if(LogFile < 0 || !connected) { - /* still not connected, retry */ - if(iRetries > 0) { - iSleep = (iRetries < 30) ? iRetries : 30; - /* we sleep a little to prevent a thight loop */ - if(Debug) - printf("multiple retries connecting to log socket" - " - doing sleep(%d)\n", iSleep); - sleep(iSleep); - } - ++iRetries; - } else { - nWritten = write(LogFile, pszRawMsg, strlen(pszRawMsg)); - if(nWritten < 0) { - /* error, recover! */ - char errStr[1024]; - printf("error writing to domain socket: %s\r\n", rs_strerror_r(errno, errStr, sizeof(errStr))); - closelog(); - } else { - /* prepare for (potential) next write */ - nToWrite -= nWritten; - iWriteOffset += nWritten; - } - } - } - - if(Debug) { - static int largest = 0; - int sz = strlen(pszRawMsg); - if(sz > largest) - largest = sz; - printf("Msg(%d/%d):%s\n\n", largest, sz, pszRawMsg); - } -} - - -/* As we are single-threaded in this example, we need - * one way to shut down the listener running on this - * single thread. We use SIG_INT to do so - it effectively - * provides a short-lived second thread ;-) - */ -void doShutdown(int i) -{ - printf("Shutting down rfc3195d. Be patient, this can take up to 30 seconds...\n"); - srAPIShutdownListener(pAPI); -} - - -/* on the the real program ;) */ -int main(int argc, char* argv[]) -{ - srRetVal iRet; - int ch; - struct sigaction sigAct; - - while ((ch = getopt(argc, argv, "di:np:r:v")) != EOF) - switch((char)ch) { - case 'd': /* debug */ - Debug = 1; - break; - case 'i': /* pid file name */ - PidFile = optarg; - break; - case 'n': /* don't fork */ - NoFork = 1; - break; - case 'p': /* path to regular log socket */ - pPathLogname = optarg; - break; - case 'r': /* listen port */ - listenPort = atoi(optarg); - if(listenPort < 1 || listenPort > 65535) { - printf("Error: invalid listen port '%s', using 601 instead\n", - optarg); - listenPort = 601; - } - break; - case 'v': - printf("rfc3195d %s.%s (using liblogging version %d.%d.%d).\n", - VERSION, PATCHLEVEL, - LIBLOGGING_VERSION_MAJOR, LIBLOGGING_VERSION_MINOR, - LIBLOGGING_VERSION_SUBMINOR); - printf("See http://www.rsyslog.com for more information.\n"); - exit(0); - case '?': - default: - usage(); - } - if ((argc -= optind)) - usage(); - - memset(&sigAct, 0, sizeof(sigAct)); - sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = doShutdown; - sigaction(SIGUSR1, &sigAct, NULL); - sigaction(SIGTERM, &sigAct, NULL); - - if(!Debug) - { - sigAct.sa_handler = SIG_IGN; - sigaction(SIGINT, &sigAct, NULL); - } - - if((pAPI = srAPIInitLib()) == NULL) - { - printf("Error initializing liblogging - aborting!\n"); - exit(1); - } - - if((iRet = srAPISetOption(pAPI, srOPTION_BEEP_LISTENPORT, listenPort)) != SR_RET_OK) - { - printf("Error %d setting listen port - aborting\n", iRet); - exit(100); - } - - if((iRet = srAPISetupListener(pAPI, OnReceive)) != SR_RET_OK) - { - printf("Error %d setting up listener - aborting\n", iRet); - exit(101); - } - - /* now move the listener to running state. Control will only - * return after SIGUSR1. - */ - if((iRet = srAPIRunListener(pAPI)) != SR_RET_OK) - { - printf("Error %d running the listener - aborting\n", iRet); - exit(102); - } - - /** control will reach this point after shutdown */ - - srAPIExitLib(pAPI); - return 0; -} -#endif /* #ifndef FEATURE_RFC3195 - main wrapper */ - -/* - * vi:set ai: - */ diff --git a/rsyslog.conf b/rsyslog.conf index 94487601..ce7d131a 100644 --- a/rsyslog.conf +++ b/rsyslog.conf @@ -46,18 +46,15 @@ local7.* /var/log/boot.log #$ActionQueueType LinkedList # run asynchronously #$ActionResumeRetryCount -1 # infinite retries if host is down # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional -#*.* @@remote-host +#*.* @@remote-host:514 # ######### Receiving Messages from Remote Hosts ########## # TCP Syslog Server: # provides TCP syslog reception and GSS-API (if compiled to support it) #$ModLoad imtcp.so # load module -# Note: as of now, you need to use the -t command line option to -# enable TCP reception (e.g. -t514 to run a server at port 514/tcp) -# This will change in later v3 releases. +#$InputTCPServerRun 514 # start up TCP listener at port 514 # UDP Syslog Server: #$ModLoad imudp.so # provides UDP syslog reception #$UDPServerRun 514 # start a UDP syslog server at standard port 514 - diff --git a/runtime/Makefile.am b/runtime/Makefile.am new file mode 100644 index 00000000..81a9d5bd --- /dev/null +++ b/runtime/Makefile.am @@ -0,0 +1,137 @@ +sbin_PROGRAMS = +man_MANS = +noinst_LTLIBRARIES = librsyslog.la +pkglib_LTLIBRARIES = +#pkglib_LTLIBRARIES = librsyslog.la + +librsyslog_la_SOURCES = \ + rsyslog.c \ + rsyslog.h \ + atomic.h \ + syslogd-types.h \ + module-template.h \ + obj-types.h \ + nsd.h \ + glbl.h \ + glbl.c \ + conf.c \ + conf.h \ + msg.c \ + msg.h \ + linkedlist.c \ + linkedlist.h \ + objomsr.c \ + objomsr.h \ + stringbuf.c \ + stringbuf.h \ + datetime.c \ + datetime.h \ + srutils.c \ + srUtils.h \ + errmsg.c \ + errmsg.h \ + debug.c \ + debug.h \ + obj.c \ + obj.h \ + modules.c \ + modules.h \ + sync.c \ + sync.h \ + expr.c \ + expr.h \ + ctok.c \ + ctok.h \ + ctok_token.c \ + ctok_token.h \ + stream.c \ + stream.h \ + var.c \ + var.h \ + wtp.c \ + wtp.h \ + wti.c \ + wti.h \ + sysvar.c \ + sysvar.h \ + vm.c \ + vm.h \ + vmstk.c \ + vmstk.h \ + vmprg.c \ + vmprg.h \ + vmop.c \ + vmop.h \ + queue.c \ + queue.h \ + cfsysline.c \ + cfsysline.h \ + \ + \ + ../action.h \ + ../action.c \ + ../threads.c \ + ../threads.h \ + \ + ../parse.c \ + ../parse.h \ + \ + ../outchannel.c \ + ../outchannel.h \ + ../template.c \ + ../template.h +# the files with ../ we need to work on - so that they either become part of the +# runtime or will no longer be needed. -- rgerhards, 2008-06-13 + +librsyslog_la_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(pthreads_cflags) +#librsyslog_la_LDFLAGS = -module -avoid-version +librsyslog_la_LIBADD = $(dl_libs) $(rt_libs) + +# +# regular expression support +# +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 +lmregexp_la_LIBADD = +endif + +if ENABLE_INET +pkglib_LTLIBRARIES += lmnet.la lmnetstrms.la +# +# network support +# +lmnet_la_SOURCES = net.c net.h +lmnet_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) +lmnet_la_LDFLAGS = -module -avoid-version +lmnet_la_LIBADD = + +# network stream master class and stream factory +lmnetstrms_la_SOURCES = netstrms.c netstrms.h netstrm.c netstrm.h nssel.c nssel.h +lmnetstrms_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) +lmnetstrms_la_LDFLAGS = -module -avoid-version +lmnetstrms_la_LIBADD = + +# netstream drivers + +# plain tcp driver - main driver +pkglib_LTLIBRARIES += lmnsd_ptcp.la +lmnsd_ptcp_la_SOURCES = nsd_ptcp.c nsd_ptcp.h nsdsel_ptcp.c nsdsel_ptcp.h +lmnsd_ptcp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) +lmnsd_ptcp_la_LDFLAGS = -module -avoid-version +lmnsd_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 nsdsel_gtls.c nsdsel_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) +endif + diff --git a/atomic.h b/runtime/atomic.h index 2421c826..430ae7f0 100644 --- a/atomic.h +++ b/runtime/atomic.h @@ -13,22 +13,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" /* autotools! */ diff --git a/cfsysline.c b/runtime/cfsysline.c index cf8e087a..c4490b48 100644 --- a/cfsysline.c +++ b/runtime/cfsysline.c @@ -7,20 +7,23 @@ * * 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 + * 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -34,7 +37,6 @@ #include <pwd.h> #include <grp.h> -#include "syslogd.h" /* TODO: when the module interface & library design is done, this should be able to go away */ #include "cfsysline.h" #include "obj.h" #include "errmsg.h" @@ -67,7 +69,7 @@ static rsRetVal doGetChar(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void * /* if we are not at a '\0', we have our new char - no validity checks here... */ if(**pp == '\0') { - errmsg.LogError(NO_ERRCODE, "No character available"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "No character available"); iRet = RS_RET_NOT_FOUND; } else { if(pSetHdlr == NULL) { @@ -131,7 +133,7 @@ static rsRetVal parseIntVal(uchar **pp, int64 *pVal) if(!isdigit((int) *p)) { errno = 0; - errmsg.LogError(NO_ERRCODE, "invalid number"); + errmsg.LogError(0, RS_RET_INVALID_INT, "invalid number"); ABORT_FINALIZE(RS_RET_INVALID_INT); } @@ -270,7 +272,7 @@ static rsRetVal doFileCreateMode(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), "value must be octal (e.g 0644)."); errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_INVALID_VALUE, "%s", errMsg); ABORT_FINALIZE(RS_RET_INVALID_VALUE); } @@ -315,7 +317,7 @@ static int doParseOnOffOption(uchar **pp) skipWhiteSpace(pp); /* skip over any whitespace */ if(getSubString(pp, (char*) szOpt, sizeof(szOpt) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "Invalid $-configline - could not extract on/off option"); + errmsg.LogError(0, NO_ERRCODE, "Invalid $-configline - could not extract on/off option"); return -1; } @@ -324,7 +326,7 @@ static int doParseOnOffOption(uchar **pp) } else if(!strcmp((char*)szOpt, "off")) { return 0; } else { - errmsg.LogError(NO_ERRCODE, "Option value must be on or off, but is '%s'", (char*)pOptStart); + errmsg.LogError(0, NO_ERRCODE, "Option value must be on or off, but is '%s'", (char*)pOptStart); return -1; } } @@ -345,14 +347,14 @@ static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p assert(*pp != NULL); if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "could not extract group name"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "could not extract group name"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } getgrnam_r((char*)szName, &gBuf, stringBuf, sizeof(stringBuf), &pgBuf); if(pgBuf == NULL) { - errmsg.LogError(NO_ERRCODE, "ID for group '%s' could not be found or error", (char*)szName); + errmsg.LogError(0, RS_RET_NOT_FOUND, "ID for group '%s' could not be found or error", (char*)szName); iRet = RS_RET_NOT_FOUND; } else { if(pSetHdlr == NULL) { @@ -387,14 +389,14 @@ static rsRetVal doGetUID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p assert(*pp != NULL); if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "could not extract user name"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "could not extract user name"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } getpwnam_r((char*)szName, &pwBuf, stringBuf, sizeof(stringBuf), &ppwBuf); if(ppwBuf == NULL) { - errmsg.LogError(NO_ERRCODE, "ID for user '%s' could not be found or error", (char*)szName); + errmsg.LogError(0, RS_RET_NOT_FOUND, "ID for user '%s' could not be found or error", (char*)szName); iRet = RS_RET_NOT_FOUND; } else { if(pSetHdlr == NULL) { @@ -909,7 +911,7 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p) iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pCmd); if(iRet == RS_RET_NOT_FOUND) { - errmsg.LogError(NO_ERRCODE, "invalid or yet-unknown config file command - have you forgotten to load a module?"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "invalid or yet-unknown config file command - have you forgotten to load a module?"); } if(iRet != RS_RET_OK) diff --git a/cfsysline.h b/runtime/cfsysline.h index 2eec18ab..07ab5fcd 100644 --- a/cfsysline.h +++ b/runtime/cfsysline.h @@ -2,22 +2,23 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 CFSYSLINE_H_INCLUDED @@ -7,6 +7,11 @@ * kept in memory only as long as the config file is actually being * processed. Thereafter, it shall be unloaded. -- rgerhards * + * TODO: the license MUST be changed to LGPL. However, we can not + * currently do that, because we use some sysklogd code to crunch + * the selector lines (e.g. *.info). That code is scheduled for removal + * as part of RainerScript. After this is done, we can change licenses. + * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * * This file is part of rsyslog. @@ -45,7 +50,8 @@ #endif #include "rsyslog.h" -#include "syslogd.h" +#include "../tools/syslogd.h" /* TODO: this must be removed! */ +#include "dirty.h" #include "parse.h" #include "action.h" #include "template.h" @@ -57,6 +63,10 @@ #include "stringbuf.h" #include "srUtils.h" #include "errmsg.h" +#include "net.h" +#include "expr.h" +#include "ctok.h" +#include "ctok_token.h" /* forward definitions */ @@ -110,7 +120,7 @@ static rsRetVal doIncludeDirectory(uchar *pDirName) ASSERT(pDirName != NULL); if((pDir = opendir((char*) pDirName)) == NULL) { - errmsg.LogError(NO_ERRCODE, "error opening include directory"); + errmsg.LogError(errno, RS_RET_FOPEN_FAILURE, "error opening include directory"); ABORT_FINALIZE(RS_RET_FOPEN_FAILURE); } @@ -185,7 +195,7 @@ doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) ASSERT(*pp != NULL); if(getSubString(pp, (char*) pattern, sizeof(pattern) / sizeof(char), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "could not extract group name"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "could not extract group name"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } @@ -232,7 +242,7 @@ doModLoad(uchar **pp, __attribute__((unused)) void* pVal) skipWhiteSpace(pp); /* skip over any whitespace */ if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "could not extract module name"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "could not extract module name"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } skipWhiteSpace(pp); /* skip over any whitespace */ @@ -279,7 +289,7 @@ doNameLine(uchar **pp, void* pVal) eDir = (enum eDirective) pVal; /* this time, it actually is NOT a pointer! */ if(getSubString(&p, szName, sizeof(szName) / sizeof(char), ',') != 0) { - errmsg.LogError(NO_ERRCODE, "Invalid config line: could not extract name - line ignored"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "Invalid config line: could not extract name - line ignored"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } if(*p == ',') @@ -328,12 +338,11 @@ cfsysline(uchar *p) { DEFiRet; uchar szCmd[64]; - uchar errMsg[128]; /* for dynamic error messages */ ASSERT(p != NULL); errno = 0; if(getSubString(&p, (char*) szCmd, sizeof(szCmd) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "Invalid $-configline - could not extract command - line ignored\n"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "Invalid $-configline - could not extract command - line ignored\n"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } @@ -351,10 +360,8 @@ cfsysline(uchar *p) skipWhiteSpace(&p); if(*p && *p != '#') { /* we have a non-whitespace, so let's complain */ - snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), + errmsg.LogError(0, NO_ERRCODE, "error: extra characters in config line ignored: '%s'", p); - errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); } finalize_it: @@ -438,7 +445,7 @@ processConfFile(uchar *pConfFile) dbgprintf("config line NOT successfully processed\n"); snprintf((char*)szErrLoc, sizeof(szErrLoc) / sizeof(uchar), "%s, line %d", pConfFile, iLnNbr); - errmsg.LogError(NO_ERRCODE, "the last error occured in %s", (char*)szErrLoc); + errmsg.LogError(0, NO_ERRCODE, "the last error occured in %s", (char*)szErrLoc); } } @@ -486,7 +493,7 @@ rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEn if(*p == ';') ++p; /* eat it */ else if(*p != '\0' && *p != '#') { - errmsg.LogError(NO_ERRCODE, "invalid character in selector line - ';template' expected"); + errmsg.LogError(0, RS_RET_ERR, "invalid character in selector line - ';template' expected"); ABORT_FINALIZE(RS_RET_ERR); } @@ -499,9 +506,7 @@ rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEn } else { /* template specified, pick it up */ if(rsCStrConstruct(&pStrB) != RS_RET_OK) { - glblHadMemShortage = 1; - iRet = RS_RET_OUT_OF_MEMORY; - goto finalize_it; + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } /* now copy the string */ @@ -562,6 +567,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int * passed back to the caller. * rgerhards 2005-09-15 */ +/* GPLv3 - stems back to sysklogd */ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f) { uchar *p; @@ -627,7 +633,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f if (pri < 0) { snprintf((char*) xbuf, sizeof(xbuf), "unknown priority name \"%s\"", buf); - errmsg.LogError(NO_ERRCODE, "%s", xbuf); + errmsg.LogError(0, RS_RET_ERR, "%s", xbuf); return RS_RET_ERR; } @@ -674,7 +680,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f if (i < 0) { snprintf((char*) xbuf, sizeof(xbuf), "unknown facility name \"%s\"", buf); - errmsg.LogError(NO_ERRCODE, "%s", xbuf); + errmsg.LogError(0, RS_RET_ERR, "%s", xbuf); return RS_RET_ERR; } @@ -780,7 +786,7 @@ dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline); finalize_it: if(iRet == RS_RET_SYNTAX_ERROR) { - errmsg.LogError(NO_ERRCODE, "syntax error in expression"); + errmsg.LogError(0, RS_RET_SYNTAX_ERROR, "syntax error in expression"); } RETiRet; @@ -810,14 +816,14 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) /* create parser object starting with line string without leading colon */ if((iRet = rsParsConstructFromSz(&pPars, (*pline)+1)) != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "Error %d constructing parser object - ignoring selector", iRet); + errmsg.LogError(0, iRet, "Error %d constructing parser object - ignoring selector", iRet); return(iRet); } /* read property */ iRet = parsDelimCStr(pPars, &f->f_filterData.prop.pCSPropName, ',', 1, 1, 1); if(iRet != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "error %d parsing filter property - ignoring selector", iRet); + errmsg.LogError(0, iRet, "error %d parsing filter property - ignoring selector", iRet); rsParsDestruct(pPars); return(iRet); } @@ -825,7 +831,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) /* read operation */ iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1, 1); if(iRet != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "error %d compare operation property - ignoring selector", iRet); + errmsg.LogError(0, iRet, "error %d compare operation property - ignoring selector", iRet); rsParsDestruct(pPars); return(iRet); } @@ -857,7 +863,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) { f->f_filterData.prop.operation = FIOP_REGEX; } else { - errmsg.LogError(NO_ERRCODE, "error: invalid compare operation '%s' - ignoring selector", + errmsg.LogError(0, NO_ERRCODE, "error: invalid compare operation '%s' - ignoring selector", (char*) rsCStrGetSzStrNoNULL(pCSCompOp)); } rsCStrDestruct(&pCSCompOp); /* no longer needed */ @@ -865,14 +871,14 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) /* read compare value */ iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue); if(iRet != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "error %d compare value property - ignoring selector", iRet); + errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet); rsParsDestruct(pPars); return(iRet); } /* skip to action part */ if((iRet = parsSkipWhitespace(pPars)) != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "error %d skipping to action part - ignoring selector", iRet); + errmsg.LogError(0, iRet, "error %d skipping to action part - ignoring selector", iRet); rsParsDestruct(pPars); return(iRet); } @@ -130,7 +130,7 @@ ctokSkipWhitespaceFromStream(ctok_t *pThis) /* we must unget the one non-whitespace we found */ CHKiRet(ctokUngetCharFromStream(pThis, c)); -dbgprintf("skipped whitepsace, stream now '%s'\n", pThis->pp); +dbgprintf("skipped whitespace, stream now '%s'\n", pThis->pp); finalize_it: RETiRet; } @@ -413,15 +413,15 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ switch(c) { case '=': /* == */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ + CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ pToken->tok = (c == '=')? ctok_CMP_EQ : ctok_INVALID; break; case '!': /* != */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ + CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ pToken->tok = (c == '=')? ctok_CMP_NEQ : ctok_INVALID; break; case '<': /* <, <=, <> */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ + CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ if(c == '=') { pToken->tok = ctok_CMP_LTEQ; } else if(c == '>') { @@ -431,7 +431,7 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) } break; case '>': /* >, >= */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ + CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ if(c == '=') { pToken->tok = ctok_CMP_GTEQ; } else { @@ -512,8 +512,9 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) /* push c back, higher level parser needs it */ CHKiRet(ctokUngetCharFromStream(pThis, c)); pToken->tok = ctok_FUNCTION; - // TODO: fill function name + /* TODO: fill function name */ } else { /* give up... */ + dbgprintf("parser has an invalid word (token) '%s'\n", szWord); pToken->tok = ctok_INVALID; } } @@ -526,6 +527,7 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) dbgoprint((obj_t*) pToken, "token: %d\n", pToken->tok); finalize_it: +/*dbgprintf("ctokGetToken, returns %d, returns token %d, addr %p\n", iRet, (*ppToken)->tok, &((*ppToken)->tok));*/ if(iRet != RS_RET_OK) { if(pToken != NULL) ctok_token.Destruct(&pToken); @@ -567,8 +569,6 @@ CODESTARTobjQueryInterface(ctok) * work here (if we can support an older interface version - that, * of course, also affects the "if" above). */ - //xxxpIf->oID = OBJctok; - pIf->Construct = ctokConstruct; pIf->ConstructFinalize = ctokConstructFinalize; pIf->Destruct = ctokDestruct; diff --git a/ctok_token.c b/runtime/ctok_token.c index 0f340675..8c17f693 100644 --- a/ctok_token.c +++ b/runtime/ctok_token.c @@ -109,8 +109,6 @@ CODESTARTobjQueryInterface(ctok_token) * work here (if we can support an older interface version - that, * of course, also affects the "if" above). */ - //xxxpIf->oID = OBJctok_token; - pIf->Construct = ctok_tokenConstruct; pIf->ConstructFinalize = ctok_tokenConstructFinalize; pIf->Destruct = ctok_tokenDestruct; diff --git a/ctok_token.h b/runtime/ctok_token.h index 346d5acd..d36689fa 100644 --- a/ctok_token.h +++ b/runtime/ctok_token.h @@ -63,11 +63,9 @@ typedef struct { ctok_CMP_STARTSWITH = 106, ctok_CMP_CONTAINSI = 107, ctok_CMP_STARTSWITHI = 108, - ctok_CMP_GTEQ = 109, /* end compare operations */ + ctok_CMP_GTEQ = 109 /* end compare operations */ } tok; var_t *pVar; - //cstr_t *pstrVal; - //int64 intVal; } ctok_token_t; diff --git a/datetime.c b/runtime/datetime.c index a178d14c..6d5652ff 100644 --- a/datetime.c +++ b/runtime/datetime.c @@ -7,22 +7,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -502,6 +503,45 @@ int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst) ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); } + +/** + * Format a syslogTimestamp to just the fractional seconds. + * The caller must provide the timestamp as well as a character + * buffer that will receive the resulting string. The function + * returns the size of the timestamp written in bytes (without + * the string terminator). If 0 is returend, an error occured. + * The buffer must be at least 10 bytes large. + * rgerhards, 2008-06-06 + */ +int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf, size_t iLenBuf) +{ + int lenRet; + char szFmtStr[64]; + + assert(ts != NULL); + assert(pBuf != NULL); + assert(iLenBuf >= 10); + + if(ts->secfracPrecision > 0) + { /* We must look at + * the precision specified. For example, if we have millisec precision (3 digits), a + * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this + * is a huge difference ;). To avoid this, we first create a format string with + * the specific precision and *then* use that format string to do the actual formating. + */ + /* be careful: there is ONE actual %d in the format string below ;) */ + snprintf(szFmtStr, sizeof(szFmtStr), "%%0%dd", ts->secfracPrecision); + lenRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->secfrac); + } else { + pBuf[0] = '0'; + pBuf[1] = '1'; + lenRet = 1; + } + + return(lenRet); +} + + /** * Format a syslogTimestamp to a RFC3339 timestamp string (as * specified in syslog-protocol). @@ -620,6 +660,7 @@ CODESTARTobjQueryInterface(datetime) pIf->ParseTIMESTAMP3164 = ParseTIMESTAMP3164; pIf->formatTimestampToMySQL = formatTimestampToMySQL; pIf->formatTimestampToPgSQL = formatTimestampToPgSQL; + pIf->formatTimestampSecFrac = formatTimestampSecFrac; pIf->formatTimestamp3339 = formatTimestamp3339; pIf->formatTimestamp3164 = formatTimestamp3164; finalize_it: diff --git a/datetime.h b/runtime/datetime.h index 9e115583..755cc0ed 100644 --- a/datetime.h +++ b/runtime/datetime.h @@ -2,22 +2,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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_DATETIME_H #define INCLUDED_DATETIME_H @@ -35,13 +36,13 @@ typedef struct datetime_s { /* interfaces */ BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */ void (*getCurrTime)(struct syslogTime *t); - //static int srSLMGParseInt32(char** ppsz); int (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS); int (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char** pszTS); int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst); int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst); int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); + int (*formatTimestampSecFrac)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); ENDinterface(datetime) #define datetimeCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ /* interface changes: diff --git a/debug.c b/runtime/debug.c index 29c65cf1..1450d029 100644 --- a/debug.c +++ b/runtime/debug.c @@ -15,22 +15,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" /* autotools! */ #include <stdio.h> @@ -742,6 +743,7 @@ sigsegvHdlr(int signum) } +#pragma GCC diagnostic ignored "-Wempty-body" /* print some debug output when an object is given * This is mostly a copy of dbgprintf, but I do not know how to combine it * into a single function as we have variable arguments and I don't know how to call @@ -833,8 +835,10 @@ dbgoprint(obj_t *pObj, char *fmt, ...) if(altdbg != NULL) fflush(altdbg); pthread_cleanup_pop(1); } +#pragma GCC diagnostic warning "-Wempty-body" +#pragma GCC diagnostic ignored "-Wempty-body" /* print some debug output when no object is given * WARNING: duplicate code, see dbgoprin above! */ @@ -907,6 +911,7 @@ dbgprintf(char *fmt, ...) if(altdbg != NULL) fflush(altdbg); pthread_cleanup_pop(1); } +#pragma GCC diagnostic warning "-Wempty-body" void tester(void) { @@ -1144,8 +1149,6 @@ dbgPrintNameAdd(uchar *pName, dbgPrintName_t **ppRoot) pEntry->pNext = *ppRoot; /* we enqueue at the front */ } *ppRoot = pEntry; - -printf("Name %s added to %p\n", pName, *ppRoot); } diff --git a/debug.h b/runtime/debug.h index 4dcc593a..214b7c05 100644 --- a/debug.h +++ b/runtime/debug.h @@ -5,22 +5,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 DEBUG_H_INCLUDED #define DEBUG_H_INCLUDED diff --git a/errmsg.c b/runtime/errmsg.c index 907046b9..3c3ee02c 100644 --- a/errmsg.c +++ b/runtime/errmsg.c @@ -1,26 +1,28 @@ /* The errmsg object. * * Module begun 2008-03-05 by Rainer Gerhards, based on some code - * from syslogd.c + * from syslogd.c. I converted this module to lgpl and have checked that + * all contributors agreed to that step. * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -31,7 +33,6 @@ #include <assert.h> #include "rsyslog.h" -#include "syslogd.h" #include "obj.h" #include "errmsg.h" #include "sysvar.h" @@ -45,11 +46,18 @@ DEFobjStaticHelpers /* ------------------------------ methods ------------------------------ */ -/* TODO: restructure this code some time. Especially look if we need - * to check errno and, if so, how to do that in a clean way. +/* We now receive three parameters: one is the internal error code + * which will also become the error message number, the second is + * errno - if it is non-zero, the corresponding error message is included + * in the text and finally the message text itself. Note that it is not + * 100% clean to use the internal errcode, as it may be reached from + * multiple actual error causes. However, it is much better than having + * no error code at all (and in most cases, a single internal error code + * maps to a specific error event). + * rgerhards, 2008-06-27 */ -static void __attribute__((format(printf, 2, 3))) -LogError(int __attribute__((unused)) iErrCode, char *fmt, ... ) +static void __attribute__((format(printf, 3, 4))) +LogError(int iErrno, int iErrCode, char *fmt, ... ) { va_list ap; char buf[1024]; @@ -73,15 +81,24 @@ LogError(int __attribute__((unused)) iErrCode, char *fmt, ... ) dbgprintf("Called LogError, msg: %s\n", buf); - if (errno == 0) { - snprintf(msg, sizeof(msg), "%s", buf); + if(iErrno != 0) { + rs_strerror_r(iErrno, errStr, sizeof(errStr)); + if(iErrCode == NO_ERRCODE) { + snprintf(msg, sizeof(msg), "%s: %s", buf, errStr); + } else { + snprintf(msg, sizeof(msg), "%s: %s [try http://www.rsyslog.com/e/%d ]", buf, errStr, iErrCode * -1); + } } else { - rs_strerror_r(errno, errStr, sizeof(errStr)); - snprintf(msg, sizeof(msg), "%s: %s", buf, errStr); + if(iErrCode == NO_ERRCODE) { + snprintf(msg, sizeof(msg), "%s", buf); + } else { + snprintf(msg, sizeof(msg), "%s [try http://www.rsyslog.com/e/%d ]", buf, iErrCode * -1); + } } msg[sizeof(msg)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ errno = 0; - logmsgInternal(LOG_SYSLOG|LOG_ERR, msg, ADDDATE); + + glblErrLogger(iErrCode, (uchar*)msg); ENDfunc } @@ -116,5 +133,12 @@ BEGINAbstractObjClassInit(errmsg, 1, OBJ_IS_CORE_MODULE) /* class, version */ /* set our own handlers */ ENDObjClassInit(errmsg) +/* Exit the class. + * rgerhards, 2008-04-17 + */ +BEGINObjClassExit(errmsg, OBJ_IS_CORE_MODULE) /* class, version */ + /* release objects we no longer need */ +ENDObjClassExit(errmsg) + /* vi:set ai: */ diff --git a/errmsg.h b/runtime/errmsg.h index 12469581..799954fb 100644 --- a/errmsg.h +++ b/runtime/errmsg.h @@ -2,22 +2,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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_ERRMSG_H #define INCLUDED_ERRMSG_H @@ -34,7 +35,7 @@ typedef struct errmsg_s { /* interfaces */ BEGINinterface(errmsg) /* name must also be changed in ENDinterface macro! */ - void __attribute__((format(printf, 2, 3))) (*LogError)(int iErrCode, char *pszErrFmt, ... ); + void __attribute__((format(printf, 3, 4))) (*LogError)(int iErrno, int iErrCode, char *pszErrFmt, ... ); ENDinterface(errmsg) #define errmsgCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ @@ -85,7 +85,7 @@ terminal(expr_t *pThis, ctok_t *tok) break; case ctok_FUNCTION: dbgoprint((obj_t*) pThis, "function\n"); - // vm: call - well, need to implement that first + /* TODO: vm: call - well, need to implement that first */ ABORT_FINALIZE(RS_RET_NOT_IMPLEMENTED); break; case ctok_MSGVAR: diff --git a/runtime/glbl.c b/runtime/glbl.c new file mode 100644 index 00000000..11a664f8 --- /dev/null +++ b/runtime/glbl.c @@ -0,0 +1,258 @@ +/* glbl.c - this module holds global defintions and data items. + * These are shared among the runtime library. Their use should be + * limited to cases where it is actually needed. The main intension for + * implementing them was support for the transistion from v2 to v4 + * (with fully modular design), but it turned out that there may also + * be some other good use cases besides backwards-compatibility. + * + * Module begun 2008-04-16 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 <sys/socket.h> +#include <assert.h> + +#include "rsyslog.h" +#include "obj.h" +#include "cfsysline.h" +#include "glbl.h" + +/* some defaults */ +#ifndef DFLT_NETSTRM_DRVR +# define DFLT_NETSTRM_DRVR ((uchar*)"ptcp") +#endif + +/* static data */ +DEFobjStaticHelpers + +/* static data + * For this object, these variables are obviously what makes the "meat" of the + * class... + */ +static uchar *pszWorkDir = NULL; +static int iDefPFFamily = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ +static int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */ +static int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */ +static int bDisableDNS = 0; /* don't look up IP addresses of remote messages */ +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 */ +static uchar *pszDfltNetstrmDrvrCAF = NULL; /* default CA file for the netstrm driver */ +static uchar *pszDfltNetstrmDrvrKeyFile = NULL; /* default key file for the netstrm driver (server) */ +static uchar *pszDfltNetstrmDrvrCertFile = NULL; /* default cert file for the netstrm driver (server) */ + + +/* define a macro for the simple properties' set and get functions + * (which are always the same). This is only suitable for pretty + * simple cases which require neither checks nor memory allocation. + */ +#define SIMP_PROP(nameFunc, nameVar, dataType) \ + SIMP_PROP_GET(nameFunc, nameVar, dataType) \ + SIMP_PROP_SET(nameFunc, nameVar, dataType) +#define SIMP_PROP_SET(nameFunc, nameVar, dataType) \ +static rsRetVal Set##nameFunc(dataType newVal) \ +{ \ + nameVar = newVal; \ + return RS_RET_OK; \ +} +#define SIMP_PROP_GET(nameFunc, nameVar, dataType) \ +static dataType Get##nameFunc(void) \ +{ \ + return(nameVar); \ +} + +SIMP_PROP(DefPFFamily, iDefPFFamily, int) /* note that in the future we may check the family argument */ +SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int) +SIMP_PROP(Option_DisallowWarning, option_DisallowWarning, int) +SIMP_PROP(DisableDNS, bDisableDNS, int) +SIMP_PROP(LocalDomain, LocalDomain, uchar*) +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 */ +SIMP_PROP_SET(DfltNetstrmDrvrCAF, pszDfltNetstrmDrvrCAF, uchar*) /* TODO: use custom function which frees existing value */ +SIMP_PROP_SET(DfltNetstrmDrvrKeyFile, pszDfltNetstrmDrvrKeyFile, uchar*) /* TODO: use custom function which frees existing value */ +SIMP_PROP_SET(DfltNetstrmDrvrCertFile, pszDfltNetstrmDrvrCertFile, uchar*) /* TODO: use custom function which frees existing value */ + +#undef SIMP_PROP +#undef SIMP_PROP_SET +#undef SIMP_PROP_GET + + +/* return our local hostname. if it is not set, "[localhost]" is returned + */ +static uchar* +GetLocalHostName(void) +{ + return(LocalHostName == NULL ? (uchar*) "[localhost]" : LocalHostName); +} + + +/* return the current working directory */ +static uchar* +GetWorkDir(void) +{ + return(pszWorkDir == NULL ? (uchar*) "" : pszWorkDir); +} + + +/* return the current default netstream driver */ +static uchar* +GetDfltNetstrmDrvr(void) +{ + return(pszDfltNetstrmDrvr == NULL ? DFLT_NETSTRM_DRVR : pszDfltNetstrmDrvr); +} + + +/* return the current default netstream driver CA File */ +static uchar* +GetDfltNetstrmDrvrCAF(void) +{ + return(pszDfltNetstrmDrvrCAF); +} + + +/* return the current default netstream driver key File */ +static uchar* +GetDfltNetstrmDrvrKeyFile(void) +{ + return(pszDfltNetstrmDrvrKeyFile); +} + + +/* return the current default netstream driver certificate File */ +static uchar* +GetDfltNetstrmDrvrCertFile(void) +{ + return(pszDfltNetstrmDrvrCertFile); +} + + +/* queryInterface function + * rgerhards, 2008-02-21 + */ +BEGINobjQueryInterface(glbl) +CODESTARTobjQueryInterface(glbl) + if(pIf->ifVersion != glblCURR_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->GetWorkDir = GetWorkDir; +#define SIMP_PROP(name) \ + pIf->Get##name = Get##name; \ + pIf->Set##name = Set##name; + SIMP_PROP(DefPFFamily); + SIMP_PROP(DropMalPTRMsgs); + SIMP_PROP(Option_DisallowWarning); + SIMP_PROP(DisableDNS); + SIMP_PROP(LocalHostName) + SIMP_PROP(LocalDomain) + SIMP_PROP(StripDomains) + SIMP_PROP(LocalHosts) + SIMP_PROP(DfltNetstrmDrvr) + SIMP_PROP(DfltNetstrmDrvrCAF) + SIMP_PROP(DfltNetstrmDrvrKeyFile) + SIMP_PROP(DfltNetstrmDrvrCertFile) +#undef SIMP_PROP +finalize_it: +ENDobjQueryInterface(glbl) + + +/* Reset config variables to default values. + * rgerhards, 2008-04-17 + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + if(pszDfltNetstrmDrvr != NULL) { + free(pszDfltNetstrmDrvr); + pszDfltNetstrmDrvr = NULL; + } + if(pszDfltNetstrmDrvrCAF != NULL) { + free(pszDfltNetstrmDrvrCAF); + pszDfltNetstrmDrvrCAF = NULL; + } + if(pszDfltNetstrmDrvrKeyFile != NULL) { + free(pszDfltNetstrmDrvrKeyFile); + pszDfltNetstrmDrvrKeyFile = NULL; + } + if(pszDfltNetstrmDrvrCertFile != NULL) { + free(pszDfltNetstrmDrvrCertFile); + pszDfltNetstrmDrvrCertFile = NULL; + } + if(pszWorkDir != NULL) { + free(pszWorkDir); + pszWorkDir = NULL; + } + bDropMalPTRMsgs = 0; + return RS_RET_OK; +} + + + +/* Initialize the glbl class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-02-19 + */ +BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */ + /* request objects we use */ + + /* register config handlers (TODO: we need to implement a way to unregister them) */ + CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"dropmsgswithmaliciousdnsptrrecords", 0, eCmdHdlrBinary, NULL, &bDropMalPTRMsgs, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdriver", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercafile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrCAF, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdriverkeyfile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrKeyFile, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercertfile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrCertFile, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL)); +ENDObjClassInit(glbl) + + +/* Exit the glbl class. + * rgerhards, 2008-04-17 + */ +BEGINObjClassExit(glbl, OBJ_IS_CORE_MODULE) /* class, version */ + if(pszDfltNetstrmDrvr != NULL) + free(pszDfltNetstrmDrvr); + if(pszDfltNetstrmDrvrCAF != NULL) + free(pszDfltNetstrmDrvrCAF); + if(pszDfltNetstrmDrvrKeyFile != NULL) + free(pszDfltNetstrmDrvrKeyFile); + if(pszDfltNetstrmDrvrCertFile != NULL) + free(pszDfltNetstrmDrvrCertFile); + if(pszWorkDir != NULL) + free(pszWorkDir); + if(LocalHostName != NULL) + free(LocalHostName); +ENDObjClassExit(glbl) + +/* vi:set ai: + */ diff --git a/runtime/glbl.h b/runtime/glbl.h new file mode 100644 index 00000000..90436319 --- /dev/null +++ b/runtime/glbl.h @@ -0,0 +1,62 @@ +/* Definition of globally-accessible data items. + * + * This module provides access methods to items of global scope. Most often, + * these globals serve as defaults to initialize local settings. Currently, + * many of them are either constants or global variable references. However, + * this module provides the necessary hooks to change that at any time. + * + * Please note that there currently is no glbl.c file as we do not yet + * have any implementations. + * + * 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 GLBL_H_INCLUDED +#define GLBL_H_INCLUDED + +#define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */ + +/* interfaces */ +BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */ + uchar* (*GetWorkDir)(void); +#define SIMP_PROP(name, dataType) \ + dataType (*Get##name)(void); \ + rsRetVal (*Set##name)(dataType); + SIMP_PROP(DefPFFamily, int) + SIMP_PROP(DropMalPTRMsgs, int) + SIMP_PROP(Option_DisallowWarning, int) + SIMP_PROP(DisableDNS, int) + SIMP_PROP(LocalHostName, uchar*) + SIMP_PROP(LocalDomain, uchar*) + SIMP_PROP(StripDomains, char**) + SIMP_PROP(LocalHosts, char**) + SIMP_PROP(DfltNetstrmDrvr, uchar*) + SIMP_PROP(DfltNetstrmDrvrCAF, uchar*) + SIMP_PROP(DfltNetstrmDrvrKeyFile, uchar*) + SIMP_PROP(DfltNetstrmDrvrCertFile, uchar*) +#undef SIMP_PROP +ENDinterface(glbl) +#define glblCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + +/* the remaining prototypes */ +PROTOTYPEObj(glbl); + +#endif /* #ifndef GLBL_H_INCLUDED */ diff --git a/linkedlist.c b/runtime/linkedlist.c index 383cf488..8f842e43 100644 --- a/linkedlist.c +++ b/runtime/linkedlist.c @@ -13,22 +13,23 @@ * * Copyright (C) 2007, 2008 by Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -409,7 +410,5 @@ finalize_it: RETiRet; } - -/* - * vi:set ai: +/* vim:set ai: */ diff --git a/linkedlist.h b/runtime/linkedlist.h index 98fb76a5..aeacd6d7 100644 --- a/linkedlist.h +++ b/runtime/linkedlist.h @@ -1,23 +1,24 @@ /* Definition of the linkedlist object. * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 LINKEDLIST_H_INCLUDED diff --git a/module-template.h b/runtime/module-template.h index 94fa1914..eb39b587 100644 --- a/module-template.h +++ b/runtime/module-template.h @@ -6,22 +6,23 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 MODULE_TEMPLATE_H_INCLUDED #define MODULE_TEMPLATE_H_INCLUDED 1 @@ -50,6 +51,9 @@ * a module provides multiple types, several separate modules must be created which * then should share a single library containing the majority of code. This macro * must be present in each module. -- rgerhards, 2007-12-14 + * Note that MODULE_TYPE_TESTBENCH is reserved for testbenches, but + * declared in their own header files (because the rest does not need these + * defines). -- rgerhards, 2008-06-13 */ #define MODULE_TYPE(x)\ static rsRetVal modGetType(eModType_t *modType) \ @@ -64,6 +68,7 @@ static rsRetVal modGetType(eModType_t *modType) \ DEF_LMOD_STATIC_DATA \ MODULE_TYPE(eMOD_LIB) + /* macro to define a unique module id. This must be able to fit in a void*. The * module id must be unique inside a running rsyslogd application. It is used to * track ownership of several objects. Most importantly, when the module is diff --git a/modules.c b/runtime/modules.c index 32a71c0c..ceb4768c 100644 --- a/modules.c +++ b/runtime/modules.c @@ -13,22 +13,23 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -48,7 +49,6 @@ #include <unistd.h> #include <sys/file.h> -#include "syslogd.h" #include "cfsysline.h" #include "modules.h" #include "errmsg.h" @@ -522,21 +522,28 @@ modUnloadAndDestructAll(eModLinkType_t modLinkTypesToUnload) if(modLinkTypesToUnload == eMOD_LINK_ALL || pModCurr->eLinkType == modLinkTypesToUnload) { if(modUnlinkAndDestroy(&pModCurr) == RS_RET_MODULE_STILL_REFERENCED) { pModCurr = GetNxt(pModCurr); + } else { + /* Note: if the module was successfully unloaded, it has updated the + * pModCurr pointer to the next module. However, the unload process may + * still have indirectly referenced the pointer list in a way that the + * unloaded module is not aware of. So we restart the unload process + * to make sure we do not fall into a trap (what we did ;)). The + * performance toll is minimal. -- rgerhards, 2008-04-28 + */ + pModCurr = GetNxt(NULL); } - /* Note: if the module was successfully unloaded, it has updated the - * pModCurr pointer to the next module. So we do NOT need to advance - * to the next module on successful unload. - */ } else { pModCurr = GetNxt(pModCurr); } } # ifdef DEBUG + /* DEV DEBUG only! if(pLoadedModules != NULL) { dbgprintf("modules still loaded after module.UnloadAndDestructAll:\n"); modUsrPrintAll(); } + */ # endif RETiRet; @@ -597,7 +604,7 @@ Load(uchar *pModName) szPath[iPathLen++] = '/'; szPath[iPathLen] = '\0'; } else { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName); + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_PATHLEN, "could not load module '%s', path too long\n", pModName); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); } } @@ -618,23 +625,23 @@ Load(uchar *pModName) } if(iPathLen + strlen((char*) pModName) >= sizeof(szPath)) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName); + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_PATHLEN, "could not load module '%s', path too long\n", pModName); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); } /* complete load path constructed, so ... GO! */ dbgprintf("loading module '%s'\n", szPath); if(!(pModHdlr = dlopen((char *) szPath, RTLD_NOW))) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlopen: %s\n", szPath, dlerror()); + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_DLOPEN, "could not load module '%s', dlopen: %s\n", szPath, dlerror()); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_DLOPEN); } if(!(pModInit = dlsym(pModHdlr, "modInit"))) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlsym: %s\n", szPath, dlerror()); + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_NO_INIT, "could not load module '%s', dlsym: %s\n", szPath, dlerror()); dlclose(pModHdlr); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_NO_INIT); } if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr)) != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', rsyslog error %d\n", szPath, iRet); + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_INIT_FAILED, "could not load module '%s', rsyslog error %d\n", szPath, iRet); dlclose(pModHdlr); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_INIT_FAILED); } diff --git a/modules.h b/runtime/modules.h index a8371d05..7d34bcf7 100644 --- a/modules.h +++ b/runtime/modules.h @@ -12,30 +12,30 @@ * * File begun on 2007-07-22 by RGerhards * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 MODULES_H_INCLUDED #define MODULES_H_INCLUDED 1 #include "objomsr.h" -#include "threads.h" /* the following define defines the current version of the module interface. @@ -7,24 +7,25 @@ * of the "old" message code without any modifications. However, it * helps to have things at the right place one we go to the meat of it. * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 <stdio.h> @@ -35,7 +36,6 @@ #include <assert.h> #include <ctype.h> #include "rsyslog.h" -#include "syslogd.h" #include "srUtils.h" #include "stringbuf.h" #include "template.h" @@ -83,7 +83,6 @@ static syslogCODE rs_facilitynames[] = { "kern", LOG_KERN }, { "lpr", LOG_LPR }, { "mail", LOG_MAIL }, - //{ "mark", INTERNAL_MARK }, /* INTERNAL */ { "news", LOG_NEWS }, { "security", LOG_AUTH }, /* DEPRECATED */ { "syslog", LOG_SYSLOG }, @@ -290,6 +289,8 @@ CODESTARTobjDestruct(msg) free(pThis->pszHOSTNAME); if(pThis->pszRcvFrom != NULL) free(pThis->pszRcvFrom); + if(pThis->pszRcvFromIP != NULL) + free(pThis->pszRcvFromIP); if(pThis->pszMSG != NULL) free(pThis->pszMSG); if(pThis->pszFacility != NULL) @@ -304,6 +305,8 @@ CODESTARTobjDestruct(msg) free(pThis->pszRcvdAt3164); if(pThis->pszRcvdAt3339 != NULL) free(pThis->pszRcvdAt3339); + if(pThis->pszRcvdAt_SecFrac != NULL) + free(pThis->pszRcvdAt_SecFrac); if(pThis->pszRcvdAt_MySQL != NULL) free(pThis->pszRcvdAt_MySQL); if(pThis->pszRcvdAt_PgSQL != NULL) @@ -312,6 +315,8 @@ CODESTARTobjDestruct(msg) free(pThis->pszTIMESTAMP3164); if(pThis->pszTIMESTAMP3339 != NULL) free(pThis->pszTIMESTAMP3339); + if(pThis->pszTIMESTAMP_SecFrac != NULL) + free(pThis->pszTIMESTAMP_SecFrac); if(pThis->pszTIMESTAMP_MySQL != NULL) free(pThis->pszTIMESTAMP_MySQL); if(pThis->pszTIMESTAMP_PgSQL != NULL) @@ -449,6 +454,7 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) objSerializePTR(pStrm, pszTAG, PSZ); objSerializePTR(pStrm, pszHOSTNAME, PSZ); objSerializePTR(pStrm, pszRcvFrom, PSZ); + objSerializePTR(pStrm, pszRcvFromIP, PSZ); objSerializePTR(pStrm, pCSStrucData, CSTR); objSerializePTR(pStrm, pCSAPPNAME, CSTR); @@ -715,7 +721,6 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszTIMESTAMP3164 == NULL) { if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -727,7 +732,6 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszTIMESTAMP_MySQL == NULL) { if((pM->pszTIMESTAMP_MySQL = malloc(15)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -739,7 +743,6 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszTIMESTAMP_PgSQL == NULL) { if((pM->pszTIMESTAMP_PgSQL = malloc(21)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -751,7 +754,6 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszTIMESTAMP3164 == NULL) { if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -763,7 +765,6 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszTIMESTAMP3339 == NULL) { if((pM->pszTIMESTAMP3339 = malloc(33)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; /* TODO: check this: can it cause a free() of constant memory?) */ } @@ -771,6 +772,17 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) } MsgUnlock(pM); return(pM->pszTIMESTAMP3339); + case tplFmtSecFrac: + MsgLock(pM); + if(pM->pszTIMESTAMP_SecFrac == NULL) { + if((pM->pszTIMESTAMP_SecFrac = malloc(10)) == NULL) { + MsgUnlock(pM); + return ""; /* TODO: check this: can it cause a free() of constant memory?) */ + } + datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac, 10); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP_SecFrac); } return "INVALID eFmt OPTION!"; } @@ -785,7 +797,6 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszRcvdAt3164 == NULL) { if((pM->pszRcvdAt3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -797,7 +808,6 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszRcvdAt_MySQL == NULL) { if((pM->pszRcvdAt_MySQL = malloc(15)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -809,7 +819,6 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszRcvdAt_PgSQL == NULL) { if((pM->pszRcvdAt_PgSQL = malloc(21)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -821,7 +830,6 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszRcvdAt3164 == NULL) { if((pM->pszRcvdAt3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -833,7 +841,6 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) MsgLock(pM); if(pM->pszRcvdAt3339 == NULL) { if((pM->pszRcvdAt3339 = malloc(33)) == NULL) { - glblHadMemShortage = 1; MsgUnlock(pM); return ""; } @@ -841,6 +848,17 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) } MsgUnlock(pM); return(pM->pszRcvdAt3339); + case tplFmtSecFrac: + MsgLock(pM); + if(pM->pszRcvdAt_SecFrac == NULL) { + if((pM->pszRcvdAt_SecFrac = malloc(10)) == NULL) { + MsgUnlock(pM); + return ""; /* TODO: check this: can it cause a free() of constant memory?) */ + } + datetime.formatTimestampSecFrac(&pM->tRcvdAt, pM->pszRcvdAt_SecFrac, 10); + } + MsgUnlock(pM); + return(pM->pszRcvdAt_SecFrac); } return "INVALID eFmt OPTION!"; } @@ -1212,6 +1230,18 @@ char *getRcvFrom(msg_t *pM) return (char*) pM->pszRcvFrom; } + +uchar *getRcvFromIP(msg_t *pM) +{ + if(pM == NULL) + return (uchar*) ""; + else + if(pM->pszRcvFromIP == NULL) + return (uchar*) ""; + else + return pM->pszRcvFromIP; +} + /* rgerhards 2004-11-24: set STRUCTURED DATA in msg object */ rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData) @@ -1385,6 +1415,24 @@ void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom) } +/* rgerhards 2005-05-16: set pszRcvFromIP in msg object */ +rsRetVal +MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP) +{ + DEFiRet; + assert(pMsg != NULL); + if(pMsg->pszRcvFromIP != NULL) { + free(pMsg->pszRcvFromIP); + pMsg->iLenRcvFromIP = 0; + } + + CHKmalloc(pMsg->pszRcvFromIP = (uchar*)strdup((char*)pszRcvFromIP)); + pMsg->iLenRcvFromIP = strlen((char*)pszRcvFromIP); +finalize_it: + RETiRet; +} + + /* Set the HOSTNAME to a caller-provided string. This is thought * to be a heap buffer that the caller will no longer use. This * function is a performance optimization over MsgSetHOSTNAME(). @@ -1529,7 +1577,6 @@ static uchar *getNOW(eNOWType eNow) struct syslogTime t; if((pBuf = (uchar*) malloc(sizeof(uchar) * tmpBUFSIZE)) == NULL) { - glblHadMemShortage = 1; return NULL; } @@ -1611,11 +1658,12 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, char *pBufStart; char *pBuf; int iLen; + short iOffs; #ifdef FEATURE_REGEXP /* Variables necessary for regular expression matching */ - size_t nmatch = 1; - regmatch_t pmatch[1]; + size_t nmatch = 10; + regmatch_t pmatch[10]; #endif assert(pMsg != NULL); @@ -1639,6 +1687,8 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, pRes = getUxTradMsg(pMsg); } else if(!strcmp((char*) pName, "fromhost")) { pRes = getRcvFrom(pMsg); + } else if(!strcmp((char*) pName, "fromhost-ip")) { + pRes = (char*) getRcvFromIP(pMsg); } else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) { pRes = getHOSTNAME(pMsg); } else if(!strcmp((char*) pName, "syslogtag")) { @@ -1846,23 +1896,64 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, /* Could not compile regex before! */ return "**NO MATCH** **BAD REGULAR EXPRESSION**"; - dbgprintf("debug: String to match for regex is: %s\n", pRes); + dbgprintf("string to match for regex is: %s\n", pRes); if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) { - if (0 != regexp.regexec(&pTpe->data.field.re, pRes, nmatch, pmatch, 0)) { + short iTry = 0; + uchar bFound = 0; + iOffs = 0; + /* first see if we find a match, iterating through the series of + * potential matches over the string. + */ + while(!bFound) { + if(regexp.regexec(&pTpe->data.field.re, pRes + iOffs, nmatch, pmatch, 0) == 0) { + if(pmatch[0].rm_so == -1) { + dbgprintf("oops ... start offset of successful regexec is -1\n"); + break; + } + if(iTry == pTpe->data.field.iMatchToUse) { + bFound = 1; + } else { + iOffs += pmatch[0].rm_eo; + ++iTry; + } + } else { + break; + } + } + if(!bFound) { /* we got no match! */ - if (*pbMustBeFreed == 1) { - free(pRes); - *pbMustBeFreed = 0; + if(pTpe->data.field.nomatchAction != TPL_REGEX_NOMATCH_USE_WHOLE_FIELD) { + if (*pbMustBeFreed == 1) { + free(pRes); + *pbMustBeFreed = 0; + } + if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR) + return "**NO MATCH**"; + else + return ""; } - return "**NO MATCH**"; } else { - /* Match! */ - /* I need to malloc pB */ + /* Match- but did it match the one we wanted? */ + /* we got no match! */ + if(pmatch[pTpe->data.field.iSubMatchToUse].rm_so == -1) { + if(pTpe->data.field.nomatchAction != TPL_REGEX_NOMATCH_USE_WHOLE_FIELD) { + if (*pbMustBeFreed == 1) { + free(pRes); + *pbMustBeFreed = 0; + } + if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR) + return "**NO MATCH**"; + else + return ""; + } + } + /* OK, we have a usable match - we now need to malloc pB */ int iLenBuf; char *pB; - iLenBuf = pmatch[0].rm_eo - pmatch[0].rm_so; + iLenBuf = pmatch[pTpe->data.field.iSubMatchToUse].rm_eo + - pmatch[pTpe->data.field.iSubMatchToUse].rm_so; pB = (char *) malloc((iLenBuf + 1) * sizeof(char)); if (pB == NULL) { @@ -1873,7 +1964,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, } /* Lets copy the matched substring to the buffer */ - memcpy(pB, pRes + pmatch[0].rm_so, iLenBuf); + memcpy(pB, pRes + iOffs + pmatch[pTpe->data.field.iSubMatchToUse].rm_so, iLenBuf); pB[iLenBuf] = '\0';/* terminate string, did not happen before */ if (*pbMustBeFreed == 1) @@ -2272,10 +2363,12 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp) MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pszTAG")) { MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszRcvFromIP")) { + MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pszRcvFrom")) { - MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pszHOSTNAME")) { MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszHOSTNAME")) { + MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pCSStrucData")) { MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pCSAPPNAME")) { @@ -5,22 +5,23 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 "template.h" /* this is a quirk, but these two are too interdependant... */ @@ -88,6 +89,8 @@ struct msg { int iLenHOSTNAME; /* Length of HOSTNAME */ uchar *pszRcvFrom; /* System message was received from */ int iLenRcvFrom; /* Length of pszRcvFrom */ + uchar *pszRcvFromIP; /* IP of system message was received from */ + int iLenRcvFromIP; /* Length of pszRcvFromIP */ short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */ cstr_t *pCSProgName; /* the (BSD) program name */ cstr_t *pCSStrucData;/* STRUCTURED-DATA */ @@ -97,6 +100,7 @@ struct msg { struct syslogTime tRcvdAt;/* time the message entered this program */ char *pszRcvdAt3164; /* time as RFC3164 formatted string (always 15 charcters) */ char *pszRcvdAt3339; /* time as RFC3164 formatted string (32 charcters at most) */ + char *pszRcvdAt_SecFrac;/* time just as fractional seconds (6 charcters) */ char *pszRcvdAt_MySQL; /* rcvdAt as MySQL formatted string (always 14 charcters) */ char *pszRcvdAt_PgSQL; /* rcvdAt as PgSQL formatted string (always 21 characters) */ struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */ @@ -104,9 +108,9 @@ struct msg { char *pszTIMESTAMP3339; /* TIMESTAMP as RFC3339 formatted string (32 charcters at most) */ char *pszTIMESTAMP_MySQL;/* TIMESTAMP as MySQL formatted string (always 14 charcters) */ char *pszTIMESTAMP_PgSQL;/* TIMESTAMP as PgSQL formatted string (always 21 characters) */ + char *pszTIMESTAMP_SecFrac;/* TIMESTAMP fractional seconds (always 6 characters) */ int msgFlags; /* flags associated with this message */ }; -typedef struct msg msg_t; /* new name */ /* function prototypes */ @@ -149,6 +153,7 @@ char *getStructuredData(msg_t *pM); int getProgramNameLen(msg_t *pM); char *getProgramName(msg_t *pM); void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom); +rsRetVal MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP); void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf); void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME); int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg); @@ -4,7 +4,7 @@ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c) * This file is under development and has not yet arrived at being fully * self-contained and a real object. So far, it is mostly an excerpt - * of the "old" message code without any modifications. However, it + * of the "old" networking code without any modifications. However, it * helps to have things at the right place one we go to the meat of it. * * Starting 2007-12-24, I have begun to shuffle more network-related code @@ -12,24 +12,32 @@ * long term, but it is good to have it out of syslogd.c. Maybe this here is * an interim location ;) * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * rgerhards, 2008-04-16: I changed this code to LGPL today. I carefully analyzed + * that it does not borrow code from the original sysklogd and that I have + * permission to do so from all other contributors. My analysis found that all + * code from sysklogd has been superseeded by our own functionality, so it + * is OK to move this file to LGPL. Some variable sysklogd variable names + * remain, but even this will change as the net object evolves. * - * Rsyslog is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -47,7 +55,6 @@ #include <fcntl.h> #include <unistd.h> -#include "syslogd.h" #include "syslogd-types.h" #include "module-template.h" #include "parse.h" @@ -61,6 +68,7 @@ MODULE_TYPE_LIB /* static data */ DEFobjStaticHelpers DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) /* support for defining allowed TCP and UDP senders. We use the same * structure to implement this (a linked list), but we define two different @@ -80,6 +88,342 @@ static struct AllowedSenders *pLastAllowedSenders_GSS = NULL; int ACLAddHostnameOnFail = 0; /* add hostname to acl when DNS resolving has failed */ int ACLDontResolve = 0; /* add hostname to acl instead of resolving it to IP(s) */ + +/* ------------------------------ begin permitted peers code ------------------------------ */ + + +/* add a wildcard entry to this permitted peer. Entries are always + * added at the tail of the list. pszStr and lenStr identify the wildcard + * entry to be added. Note that the string is NOT \0 terminated, so + * we must rely on lenStr for when it is finished. + * rgerhards, 2008-05-27 + */ +static rsRetVal +AddPermittedPeerWildcard(permittedPeers_t *pPeer, uchar* pszStr, size_t lenStr) +{ + permittedPeerWildcard_t *pNew = NULL; + size_t iSrc; + size_t iDst; + DEFiRet; + + assert(pPeer != NULL); + assert(pszStr != NULL); + + CHKmalloc(pNew = calloc(1, sizeof(permittedPeers_t))); + + if(lenStr == 0) { /* empty domain components are permitted */ + pNew->wildcardType = PEER_WILDCARD_EMPTY_COMPONENT; + FINALIZE; + } else { + /* alloc memory for the domain component. We may waste a byte or + * two, but that's ok. + */ + CHKmalloc(pNew->pszDomainPart = malloc(lenStr +1 )); + } + + if(pszStr[0] == '*') { + pNew->wildcardType = PEER_WILDCARD_AT_START; + iSrc = 1; /* skip '*' */ + } else { + iSrc = 0; + } + + for(iDst = 0 ; iSrc < lenStr && pszStr[iSrc] != '*' ; ++iSrc, ++iDst) { + pNew->pszDomainPart[iDst] = pszStr[iSrc]; + } + + if(iSrc < lenStr) { + if(iSrc + 1 == lenStr && pszStr[iSrc] == '*') { + if(pNew->wildcardType == PEER_WILDCARD_AT_START) { + ABORT_FINALIZE(RS_RET_INVALID_WILDCARD); + } else { + pNew->wildcardType = PEER_WILDCARD_AT_END; + } + } else { + /* we have an invalid wildcard, something follows the asterisk! */ + ABORT_FINALIZE(RS_RET_INVALID_WILDCARD); + } + } + + if(lenStr == 1 && pNew->wildcardType == PEER_WILDCARD_AT_START) { + pNew->wildcardType = PEER_WILDCARD_MATCH_ALL; + } + + /* if we reach this point, we had a valid wildcard. We now need to + * properly terminate the domain component string. + */ + pNew->pszDomainPart[iDst] = '\0'; + pNew->lenDomainPart = strlen((char*)pNew->pszDomainPart); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pNew != NULL) { + if(pNew->pszDomainPart != NULL) + free(pNew->pszDomainPart); + free(pNew); + } + } else { + /* enqueue the element */ + if(pPeer->pWildcardRoot == NULL) { + pPeer->pWildcardRoot = pNew; + } else { + pPeer->pWildcardLast->pNext = pNew; + } + pPeer->pWildcardLast = pNew; + } + + RETiRet; +} + + +/* Destruct a permitted peer's wildcard list -- rgerhards, 2008-05-27 */ +static rsRetVal +DestructPermittedPeerWildcards(permittedPeers_t *pPeer) +{ + permittedPeerWildcard_t *pCurr; + permittedPeerWildcard_t *pDel; + DEFiRet; + + assert(pPeer != NULL); + + for(pCurr = pPeer->pWildcardRoot ; pCurr != NULL ; /*EMPTY*/) { + pDel = pCurr; + pCurr = pCurr->pNext; + free(pDel->pszDomainPart); + free(pDel); + } + + pPeer->pWildcardRoot = NULL; + pPeer->pWildcardLast = NULL; + + RETiRet; +} + + +/* add a permitted peer. PermittedPeers is an interim solution until we can provide + * access control via enhanced RainerScript methods. + * Note: the provided string is handed over to this function, caller must + * no longer access it. -- rgerhards, 2008-05-19 + */ +static rsRetVal +AddPermittedPeer(permittedPeers_t **ppRootPeer, uchar* pszID) +{ + permittedPeers_t *pNew = NULL; + DEFiRet; + + assert(ppRootPeer != NULL); + assert(pszID != NULL); + + CHKmalloc(pNew = calloc(1, sizeof(permittedPeers_t))); /* we use calloc() for consistency with "real" objects */ + CHKmalloc(pNew->pszID = (uchar*)strdup((char*)pszID)); + + if(*ppRootPeer != NULL) { + pNew->pNext = *ppRootPeer; + } + *ppRootPeer = pNew; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pNew != NULL) + free(pNew); + } + RETiRet; +} + + +/* Destruct a permitted peers list -- rgerhards, 2008-05-19 */ +static rsRetVal +DestructPermittedPeers(permittedPeers_t **ppRootPeer) +{ + permittedPeers_t *pCurr; + permittedPeers_t *pDel; + DEFiRet; + + assert(ppRootPeer != NULL); + + for(pCurr = *ppRootPeer ; pCurr != NULL ; /*EMPTY*/) { + pDel = pCurr; + pCurr = pCurr->pNext; + DestructPermittedPeerWildcards(pDel); + free(pDel->pszID); + free(pDel); + } + + *ppRootPeer = NULL; + + RETiRet; +} + + +/* Compile a wildcard. The function first checks if there is a wildcard + * present and compiles it only if so ;) It sets the etryType status + * accordingly. + * rgerhards, 2008-05-27 + */ +static rsRetVal +PermittedPeerWildcardCompile(permittedPeers_t *pPeer) +{ + uchar *pC; + uchar *pStart; + DEFiRet; + + assert(pPeer != NULL); + assert(pPeer->pszID != NULL); + + /* first check if we have a wildcard */ + for(pC = pPeer->pszID ; *pC != '\0' && *pC != '*' ; ++pC) + /*EMPTY, just skip*/; + + if(*pC == '\0') { + /* no wildcard found, we are mostly done */ + pPeer->etryType = PERM_PEER_TYPE_PLAIN; + FINALIZE; + } + + /* if we reach this point, the string contains wildcards. So let's + * compile the structure. To do so, we must parse from dot to dot + * and create a wildcard entry for each domain component we find. + * We must also flag problems if we have an asterisk in the middle + * of the text (it is supported at the start or end only). + */ + pPeer->etryType = PERM_PEER_TYPE_WILDCARD; + pC = pPeer->pszID; + while(*pC != '\0') { + pStart = pC; + /* find end of domain component */ + for( ; *pC != '\0' && *pC != '.' ; ++pC) + /*EMPTY, just skip*/; + CHKiRet(AddPermittedPeerWildcard(pPeer, pStart, pC - pStart)); + /* now check if we have an empty component at end of string */ + if(*pC == '.' && *(pC + 1) == '\0') { + /* pStart is a dummy, it is not used if length is 0 */ + CHKiRet(AddPermittedPeerWildcard(pPeer, pStart, 0)); + } + if(*pC != '\0') + ++pC; + } + +finalize_it: + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "error compiling wildcard expression '%s'", + pPeer->pszID); + } + RETiRet; +} + + +/* Do a (potential) wildcard match. The function first checks if the wildcard + * has already been compiled and, if not, compiles it. If the peer entry in + * question does NOT contain a wildcard, a simple strcmp() is done. + * *pbIsMatching is set to 0 if there is no match and something else otherwise. + * rgerhards, 2008-05-27 */ +static rsRetVal +PermittedPeerWildcardMatch(permittedPeers_t *pPeer, uchar *pszNameToMatch, int *pbIsMatching) +{ + permittedPeerWildcard_t *pWildcard; + uchar *pC; + uchar *pStart; /* start of current domain component */ + size_t iWildcard, iName; /* work indexes for backward comparisons */ + DEFiRet; + + assert(pPeer != NULL); + assert(pszNameToMatch != NULL); + assert(pbIsMatching != NULL); + + if(pPeer->etryType == PERM_PEER_TYPE_UNDECIDED) { + PermittedPeerWildcardCompile(pPeer); + } + + if(pPeer->etryType == PERM_PEER_TYPE_PLAIN) { + *pbIsMatching = !strcmp((char*)pPeer->pszID, (char*)pszNameToMatch); + FINALIZE; + } + + /* we have a wildcard, so we need to extract the domain components and + * check then against the provided wildcards. + */ + pWildcard = pPeer->pWildcardRoot; + pC = pszNameToMatch; + while(*pC != '\0') { + if(pWildcard == NULL) { + /* we have more domain components than we have wildcards --> no match */ + *pbIsMatching = 0; + FINALIZE; + } + pStart = pC; + while(*pC != '\0' && *pC != '.') { + ++pC; + } + + /* got the component, now do the match */ + switch(pWildcard->wildcardType) { + case PEER_WILDCARD_NONE: + if( pWildcard->lenDomainPart != (size_t) (pC - pStart) + || strncmp((char*)pStart, (char*)pWildcard->pszDomainPart, pC - pStart)) { + *pbIsMatching = 0; + FINALIZE; + } + break; + case PEER_WILDCARD_AT_START: + /* we need to do the backwards-matching manually */ + if(pWildcard->lenDomainPart > (size_t) (pC - pStart)) { + *pbIsMatching = 0; + FINALIZE; + } + iName = (size_t) (pC - pStart) - pWildcard->lenDomainPart; + iWildcard = 0; + while(iWildcard < pWildcard->lenDomainPart) { + if(pWildcard->pszDomainPart[iWildcard] != pStart[iName]) { + *pbIsMatching = 0; + FINALIZE; + } + ++iName; + ++iWildcard; + } + break; + case PEER_WILDCARD_AT_END: + if( pWildcard->lenDomainPart > (size_t) (pC - pStart) + || strncmp((char*)pStart, (char*)pWildcard->pszDomainPart, pWildcard->lenDomainPart)) { + *pbIsMatching = 0; + FINALIZE; + } + break; + case PEER_WILDCARD_MATCH_ALL: + /* everything is OK, just continue */ + break; + case PEER_WILDCARD_EMPTY_COMPONENT: + if(pC - pStart > 0) { + /* if it is not empty, it is no match... */ + *pbIsMatching = 0; + FINALIZE; + } + break; + } + pWildcard = pWildcard->pNext; /* we processed this entry */ + + /* skip '.' if we had it and so prepare for next iteration */ + if(*pC == '.') + ++pC; + } + + if(pWildcard != NULL) { + /* we have more domain components than in the name to be + * checked. So this is no match. + */ + *pbIsMatching = 0; + FINALIZE; + } + + *pbIsMatching = 1; /* finally... it matches ;) */ + +finalize_it: + RETiRet; +} + + +/* ------------------------------ end permitted peers code ------------------------------ */ + + /* Code for handling allowed/disallowed senders */ static inline void MaskIP6 (struct in6_addr *addr, uint8_t bits) { @@ -123,7 +467,6 @@ static rsRetVal AddAllowedSenderEntry(struct AllowedSenders **ppRoot, struct All assert(iAllow != NULL); if((pEntry = (struct AllowedSenders*) calloc(1, sizeof(struct AllowedSenders))) == NULL) { - glblHadMemShortage = 1; return RS_RET_OUT_OF_MEMORY; /* no options left :( */ } @@ -188,14 +531,14 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS /* we handle this seperatly just to provide a better * error message. */ - errmsg.LogError(NO_ERRCODE, "You can not specify 0 bits of the netmask, this would " + errmsg.LogError(0, NO_ERRCODE, "You can not specify 0 bits of the netmask, this would " "match ALL systems. If you really intend to do that, " "remove all $AllowedSender directives."); switch (iAllow->addr.NetAddr->sa_family) { case AF_INET: if((iSignificantBits < 1) || (iSignificantBits > 32)) { - errmsg.LogError(NO_ERRCODE, "Invalid number of bits (%d) in IPv4 address - adjusted to 32", + errmsg.LogError(0, NO_ERRCODE, "Invalid number of bits (%d) in IPv4 address - adjusted to 32", (int)iSignificantBits); iSignificantBits = 32; } @@ -204,7 +547,7 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS break; case AF_INET6: if((iSignificantBits < 1) || (iSignificantBits > 128)) { - errmsg.LogError(NO_ERRCODE, "Invalid number of bits (%d) in IPv6 address - adjusted to 128", + errmsg.LogError(0, NO_ERRCODE, "Invalid number of bits (%d) in IPv6 address - adjusted to 128", iSignificantBits); iSignificantBits = 128; } @@ -219,7 +562,7 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS * worst thing that happens is that one host will not be allowed to * log. */ - errmsg.LogError(NO_ERRCODE, "Internal error caused AllowedSender to be ignored, AF = %d", + errmsg.LogError(0, NO_ERRCODE, "Internal error caused AllowedSender to be ignored, AF = %d", iAllow->addr.NetAddr->sa_family); ABORT_FINALIZE(RS_RET_ERR); } @@ -227,8 +570,8 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS iRet = AddAllowedSenderEntry(ppRoot, ppLast, iAllow, iSignificantBits); } else { /* we need to process a hostname ACL */ - if (DisableDNS) { - errmsg.LogError(NO_ERRCODE, "Ignoring hostname based ACLs because DNS is disabled."); + if(glbl.GetDisableDNS()) { + errmsg.LogError(0, NO_ERRCODE, "Ignoring hostname based ACLs because DNS is disabled."); ABORT_FINALIZE(RS_RET_OK); } @@ -249,14 +592,14 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS # endif if (getaddrinfo (iAllow->addr.HostWildcard, NULL, &hints, &res) != 0) { - errmsg.LogError(NO_ERRCODE, "DNS error: Can't resolve \"%s\"", iAllow->addr.HostWildcard); + errmsg.LogError(0, NO_ERRCODE, "DNS error: Can't resolve \"%s\"", iAllow->addr.HostWildcard); if (ACLAddHostnameOnFail) { - errmsg.LogError(NO_ERRCODE, "Adding hostname \"%s\" to ACL as a wildcard entry.", iAllow->addr.HostWildcard); + errmsg.LogError(0, NO_ERRCODE, "Adding hostname \"%s\" to ACL as a wildcard entry.", iAllow->addr.HostWildcard); iRet = AddAllowedSenderEntry(ppRoot, ppLast, iAllow, iSignificantBits); FINALIZE; } else { - errmsg.LogError(NO_ERRCODE, "Hostname \"%s\" WON\'T be added to ACL.", iAllow->addr.HostWildcard); + errmsg.LogError(0, NO_ERRCODE, "Hostname \"%s\" WON\'T be added to ACL.", iAllow->addr.HostWildcard); ABORT_FINALIZE(RS_RET_NOENTRY); } } @@ -267,7 +610,6 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS iSignificantBits = 32; allowIP.flags = 0; if((allowIP.addr.NetAddr = malloc(res->ai_addrlen)) == NULL) { - glblHadMemShortage = 1; ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } memcpy(allowIP.addr.NetAddr, res->ai_addr, res->ai_addrlen); @@ -284,7 +626,6 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS allowIP.flags = 0; if((allowIP.addr.NetAddr = malloc(sizeof(struct sockaddr_in))) == NULL) { - glblHadMemShortage = 1; ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } SIN(allowIP.addr.NetAddr)->sin_family = AF_INET; @@ -306,7 +647,6 @@ static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedS iSignificantBits = 128; allowIP.flags = 0; if((allowIP.addr.NetAddr = malloc(res->ai_addrlen)) == NULL) { - glblHadMemShortage = 1; ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } memcpy(allowIP.addr.NetAddr, res->ai_addr, res->ai_addrlen); @@ -416,7 +756,7 @@ rsRetVal addAllowedSenderLine(char* pName, uchar** ppRestOfConfLine) ppLast = &pLastAllowedSenders_GSS; #endif } else { - errmsg.LogError(NO_ERRCODE, "Invalid protocol '%s' in allowed sender " + errmsg.LogError(0, RS_RET_ERR, "Invalid protocol '%s' in allowed sender " "list, line ignored", pName); return RS_RET_ERR; } @@ -427,7 +767,7 @@ rsRetVal addAllowedSenderLine(char* pName, uchar** ppRestOfConfLine) */ /* create parser object starting with line string without leading colon */ if((iRet = rsParsConstructFromSz(&pPars, (uchar*) *ppRestOfConfLine) != RS_RET_OK)) { - errmsg.LogError(NO_ERRCODE, "Error %d constructing parser object - ignoring allowed sender list", iRet); + errmsg.LogError(0, iRet, "Error %d constructing parser object - ignoring allowed sender list", iRet); return(iRet); } @@ -436,18 +776,17 @@ rsRetVal addAllowedSenderLine(char* pName, uchar** ppRestOfConfLine) break; /* a comment-sign stops processing of line */ /* now parse a single IP address */ if((iRet = parsAddrWithBits(pPars, &uIP, &iBits)) != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "Error %d parsing address in allowed sender" + errmsg.LogError(0, iRet, "Error %d parsing address in allowed sender" "list - ignoring.", iRet); rsParsDestruct(pPars); return(iRet); } - if((iRet = AddAllowedSender(ppRoot, ppLast, uIP, iBits)) - != RS_RET_OK) { - if (iRet == RS_RET_NOENTRY) { - errmsg.LogError(NO_ERRCODE, "Error %d adding allowed sender entry " + if((iRet = AddAllowedSender(ppRoot, ppLast, uIP, iBits)) != RS_RET_OK) { + if(iRet == RS_RET_NOENTRY) { + errmsg.LogError(0, iRet, "Error %d adding allowed sender entry " "- ignoring.", iRet); } else { - errmsg.LogError(NO_ERRCODE, "Error %d adding allowed sender entry " + errmsg.LogError(0, iRet, "Error %d adding allowed sender entry " "- terminating, nothing more will be added.", iRet); rsParsDestruct(pPars); return(iRet); @@ -622,6 +961,8 @@ should_use_so_bsdcompat(void) * but has been moved out of it because of clarity and fuctional separation. * 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. + * 2008-05-16 rgerhards: added field for IP address representation. Must also + * be NI_MAXHOST size large. * * Please see http://www.hmug.org/man/3/getnameinfo.php (under Caveats) * for some explanation of the code found below. We do by default not @@ -631,27 +972,27 @@ should_use_so_bsdcompat(void) * message should be processed (1) or discarded (0). */ static rsRetVal -gethname(struct sockaddr_storage *f, uchar *pszHostFQDN) +gethname(struct sockaddr_storage *f, uchar *pszHostFQDN, uchar *ip) { DEFiRet; int error; sigset_t omask, nmask; - char ip[NI_MAXHOST]; struct addrinfo hints, *res; assert(f != NULL); assert(pszHostFQDN != NULL); error = getnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *)f), - ip, sizeof ip, NULL, 0, NI_NUMERICHOST); + (char*) ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (error) { dbgprintf("Malformed from address %s\n", gai_strerror(error)); strcpy((char*) pszHostFQDN, "???"); + strcpy((char*) ip, "???"); ABORT_FINALIZE(RS_RET_INVALID_SOURCE); } - if (!DisableDNS) { + if(!glbl.GetDisableDNS()) { sigemptyset(&nmask); sigaddset(&nmask, SIGHUP); pthread_sigmask(SIG_BLOCK, &nmask, &omask); @@ -662,7 +1003,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 @@ -680,12 +1020,12 @@ gethname(struct sockaddr_storage *f, uchar *pszHostFQDN) * time being, we simply drop the name we obtained and use the IP - that one * is OK in any way. We do also log the error message. rgerhards, 2007-07-16 */ - if(bDropMalPTRMsgs == 1) { + if(glbl.GetDropMalPTRMsgs() == 1) { snprintf((char*)szErrMsg, sizeof(szErrMsg) / sizeof(uchar), "Malicious PTR record, message dropped " "IP = \"%s\" HOST = \"%s\"", ip, pszHostFQDN); - errmsg.LogError(NO_ERRCODE, "%s", szErrMsg); + errmsg.LogError(0, RS_RET_MALICIOUS_ENTITY, "%s", szErrMsg); pthread_sigmask(SIG_SETMASK, &omask, NULL); ABORT_FINALIZE(RS_RET_MALICIOUS_ENTITY); } @@ -700,7 +1040,7 @@ gethname(struct sockaddr_storage *f, uchar *pszHostFQDN) "Malicious PTR record (message accepted, but used IP " "instead of PTR name: IP = \"%s\" HOST = \"%s\"", ip, pszHostFQDN); - errmsg.LogError(NO_ERRCODE, "%s", szErrMsg); + errmsg.LogError(0, NO_ERRCODE, "%s", szErrMsg); error = 1; /* that will trigger using IP address below. */ } @@ -708,9 +1048,9 @@ gethname(struct sockaddr_storage *f, uchar *pszHostFQDN) pthread_sigmask(SIG_SETMASK, &omask, NULL); } - if (error || DisableDNS) { + if(error || glbl.GetDisableDNS()) { dbgprintf("Host name for your address (%s) unknown\n", ip); - strcpy((char*) pszHostFQDN, ip); + strcpy((char*) pszHostFQDN, (char*)ip); ABORT_FINALIZE(RS_RET_ADDRESS_UNKNOWN); } @@ -770,8 +1110,9 @@ void debugListenInfo(int fd, char *type) * there is no way to check it. We use this way of doing things because it * frees us from using dynamic memory allocation where it really does not * pay. + * 2005-05-16 rgerhards: added IP representation. Must also be NI_MAXHOST */ -rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN) +rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN, uchar *pszIP) { DEFiRet; register uchar *p; @@ -781,7 +1122,7 @@ rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN assert(pszHost != NULL); assert(pszHostFQDN != NULL); - iRet = gethname(f, pszHostFQDN); + iRet = gethname(f, pszHostFQDN, pszIP); if(iRet == RS_RET_INVALID_SOURCE || iRet == RS_RET_ADDRESS_UNKNOWN) { strcpy((char*) pszHost, (char*) pszHostFQDN); /* we use whatever was provided as replacement */ @@ -792,9 +1133,8 @@ rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN /* if we reach this point, we obtained a non-numeric hostname and can now process it */ - /* Convert to lower case, just like LocalDomain above - */ - for (p = pszHostFQDN ; *p ; p++) + /* Convert to lower case */ + for(p = pszHostFQDN ; *p ; p++) if (isupper((int) *p)) *p = tolower(*p); @@ -810,16 +1150,19 @@ rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN */ strcpy((char*)pszHost, (char*)pszHostFQDN); if ((p = (uchar*) strchr((char*)pszHost, '.'))) { /* find start of domain name "machine.example.com" */ - if(strcmp((char*) (p + 1), LocalDomain) == 0) { + if(strcmp((char*)(p + 1), (char*)glbl.GetLocalDomain()) == 0) { *p = '\0'; /* simply terminate the string */ } else { /* now check if we belong to any of the domain names that were specified * in the -s command line option. If so, remove and we are done. + * TODO: this must go away! -- rgerhards, 2008-04-16 + * For proper modularization, this must be done different, e.g. via a + * "to be stripped" property of *this* object itself. */ - if (StripDomains) { + if(glbl.GetStripDomains() != NULL) { count=0; - while (StripDomains[count]) { - if (strcmp((char*)(p + 1), StripDomains[count]) == 0) { + while(glbl.GetStripDomains()[count]) { + if (strcmp((char*)(p + 1), glbl.GetStripDomains()[count]) == 0) { *p = '\0'; FINALIZE; /* we are done */ } @@ -833,11 +1176,12 @@ rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN * door would be wide-open for all kinds of mixing up of hosts. Because of this, * you'll see comparison against the full string (pszHost) below. The termination * still occurs at *p, which points at the first dot after the hostname. + * TODO: this must also go away - see comment above -- rgerhards, 2008-04-16 */ - if (LocalHosts) { + if(glbl.GetLocalHosts() != NULL) { count=0; - while (LocalHosts[count]) { - if (!strcmp((char*)pszHost, LocalHosts[count])) { + while (glbl.GetLocalHosts()[count]) { + if (!strcmp((char*)pszHost, (char*)glbl.GetLocalHosts()[count])) { *p = '\0'; break; /* we are done */ } @@ -923,12 +1267,12 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; else hints.ai_flags = AI_NUMERICSERV; - hints.ai_family = family; + hints.ai_family = glbl.GetDefPFFamily(); hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo((char*) hostname, (char*) pszPort, &hints, &res); if(error) { - errmsg.LogError(NO_ERRCODE, "%s", gai_strerror(error)); - errmsg.LogError(NO_ERRCODE, "UDP message reception disabled due to error logged in last message.\n"); + errmsg.LogError(0, NO_ERRCODE, "%s", gai_strerror(error)); + errmsg.LogError(0, NO_ERRCODE, "UDP message reception disabled due to error logged in last message.\n"); return NULL; } @@ -937,7 +1281,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) /* EMPTY */; socks = malloc((maxs+1) * sizeof(int)); if (socks == NULL) { - errmsg.LogError(NO_ERRCODE, "couldn't allocate memory for UDP sockets, suspending UDP message reception"); + errmsg.LogError(0, NO_ERRCODE, "couldn't allocate memory for UDP sockets, suspending UDP message reception"); freeaddrinfo(res); return NULL; } @@ -948,7 +1292,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) *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_udp_socket(), socket"); + errmsg.LogError(errno, NO_ERRCODE, "create_udp_socket(), socket"); /* it is debateble if PF_INET with EAFNOSUPPORT should * also be ignored... */ @@ -960,7 +1304,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) int ion = 1; if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ion, sizeof (ion)) < 0) { - errmsg.LogError(NO_ERRCODE, "setsockopt"); + errmsg.LogError(errno, NO_ERRCODE, "setsockopt"); close(*s); *s = -1; continue; @@ -977,7 +1321,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) */ if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) { - errmsg.LogError(NO_ERRCODE, "setsockopt(REUSEADDR)"); + errmsg.LogError(errno, NO_ERRCODE, "setsockopt(REUSEADDR)"); close(*s); *s = -1; continue; @@ -990,7 +1334,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) if (should_use_so_bsdcompat()) { if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT, (char *) &on, sizeof(on)) < 0) { - errmsg.LogError(NO_ERRCODE, "setsockopt(BSDCOMPAT)"); + errmsg.LogError(errno, NO_ERRCODE, "setsockopt(BSDCOMPAT)"); close(*s); *s = -1; continue; @@ -1012,7 +1356,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) sockflags = fcntl(*s, F_SETFL, sockflags); } if (sockflags == -1) { - errmsg.LogError(NO_ERRCODE, "fcntl(O_NONBLOCK)"); + errmsg.LogError(errno, NO_ERRCODE, "fcntl(O_NONBLOCK)"); close(*s); *s = -1; continue; @@ -1031,7 +1375,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) && (errno != EADDRINUSE) # endif ) { - errmsg.LogError(NO_ERRCODE, "bind"); + errmsg.LogError(errno, NO_ERRCODE, "bind"); close(*s); *s = -1; continue; @@ -1050,7 +1394,7 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer) "- this may or may not be an error indication.\n", *socks, maxs); if(*socks == 0) { - errmsg.LogError(NO_ERRCODE, "No UDP listen socket could successfully be initialized, " + errmsg.LogError(0, NO_ERRCODE, "No UDP listen socket could successfully be initialized, " "message reception via UDP disabled.\n"); /* we do NOT need to free any sockets, because there were none... */ free(socks); @@ -1086,6 +1430,9 @@ CODESTARTobjQueryInterface(net) pIf->isAllowedSender = isAllowedSender; pIf->should_use_so_bsdcompat = should_use_so_bsdcompat; pIf->getLocalHostname = getLocalHostname; + pIf->AddPermittedPeer = AddPermittedPeer; + pIf->DestructPermittedPeers = DestructPermittedPeers; + pIf->PermittedPeerWildcardMatch = PermittedPeerWildcardMatch; finalize_it: ENDobjQueryInterface(net) @@ -1096,6 +1443,7 @@ ENDobjQueryInterface(net) BEGINObjClassExit(net, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ CODESTARTObjClassExit(net) /* release objects we no longer need */ + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); ENDObjClassExit(net) @@ -1107,6 +1455,7 @@ ENDObjClassExit(net) BEGINAbstractObjClassInit(net, 1, OBJ_IS_CORE_MODULE) /* class, version */ /* request objects we use */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); /* set our own handlers */ ENDObjClassInit(net) @@ -1,32 +1,37 @@ /* Definitions for network-related stuff. * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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_NET_H #define INCLUDED_NET_H -#ifdef SYSLOG_INET #include <netinet/in.h> #include <sys/socket.h> /* this is needed on HP UX -- rgerhards, 2008-03-04 */ +typedef enum _TCPFRAMINGMODE { + TCP_FRAMING_OCTET_STUFFING = 0, /* traditional LF-delimited */ + TCP_FRAMING_OCTET_COUNTING = 1 /* -transport-tls like octet count */ + } TCPFRAMINGMODE; + #define F_SET(where, flag) (where)|=(flag) #define F_ISSET(where, flag) ((where)&(flag))==(flag) #define F_UNSET(where, flag) (where)&=~(flag) @@ -49,7 +54,7 @@ struct NetAddr { }; #ifndef SO_BSDCOMPAT - /* this shall prevent compiler errors due to undfined name */ + /* this shall prevent compiler errors due to undefined name */ # define SO_BSDCOMPAT 0 #endif @@ -86,9 +91,47 @@ struct AllowedSenders { }; +/* this structure is a helper to implement wildcards in permittedPeers_t. It specifies + * the domain component and the matching mode. + * rgerhards, 2008-05-27 + */ +struct permittedPeerWildcard_s { + uchar *pszDomainPart; + size_t lenDomainPart; + enum { + PEER_WILDCARD_NONE = 0, /**< no wildcard in this entry */ + PEER_WILDCARD_AT_START = 1, /**< wildcard at start of entry (*name) */ + PEER_WILDCARD_AT_END = 2, /**< wildcard at end of entry (name*) */ + PEER_WILDCARD_MATCH_ALL = 3, /**< only * wildcard, matches all values */ + PEER_WILDCARD_EMPTY_COMPONENT = 4/**< special case: domain component empty (e.g. "..") */ + } wildcardType; + permittedPeerWildcard_t *pNext; +}; + +/* for fingerprints and hostnames, we need to have a temporary linked list of + * permitted values. Unforutnately, we must also duplicate this in the netstream + * drivers. However, this is the best interim solution (with the least effort). + * A clean implementation requires that we have more capable variables and the + * full-fledged scripting engine available. So we have opted to do the interim + * solution so that our users can begin to enjoy authenticated TLS. The next step + * (hopefully) is to enhance RainerScript. -- rgerhards, 2008-05-19 + */ +struct permittedPeers_s { + uchar *pszID; + enum { + PERM_PEER_TYPE_UNDECIDED = 0, /**< we have not yet decided the type (fine in some auth modes) */ + PERM_PEER_TYPE_PLAIN = 1, /**< just plain text contained */ + PERM_PEER_TYPE_WILDCARD = 2, /**< wildcards are contained, wildcard struture is filled */ + } etryType; + permittedPeers_t *pNext; + permittedPeerWildcard_t *pWildcardRoot; /**< root of the wildcard, NULL if not initialized */ + permittedPeerWildcard_t *pWildcardLast; /**< end of the wildcard list, NULL if not initialized */ +}; + + /* interfaces */ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */ - rsRetVal (*cvthname)(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN); + rsRetVal (*cvthname)(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN, uchar *pszIP); /* things to go away after proper modularization */ rsRetVal (*addAllowedSenderLine)(char* pName, uchar** ppRestOfConfLine); void (*PrintAllowedSenders)(int iListToPrint); @@ -99,14 +142,18 @@ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */ int (*isAllowedSender)(struct AllowedSenders *pAllowRoot, struct sockaddr *pFrom, const char *pszFromHost); rsRetVal (*getLocalHostname)(uchar**); int (*should_use_so_bsdcompat)(void); - /* data memebers - these should go away over time... TODO */ + /* permitted peer handling should be replaced by something better (see comments above) */ + rsRetVal (*AddPermittedPeer)(permittedPeers_t **ppRootPeer, uchar *pszID); + rsRetVal (*DestructPermittedPeers)(permittedPeers_t **ppRootPeer); + rsRetVal (*PermittedPeerWildcardMatch)(permittedPeers_t *pPeer, uchar *pszNameToMatch, int *pbIsMatching); + /* data members - these should go away over time... TODO */ int *pACLAddHostnameOnFail; /* add hostname to acl when DNS resolving has failed */ int *pACLDontResolve; /* add hostname to acl instead of resolving it to IP(s) */ struct AllowedSenders *pAllowedSenders_UDP; struct AllowedSenders *pAllowedSenders_TCP; struct AllowedSenders *pAllowedSenders_GSS; ENDinterface(net) -#define netCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ +#define netCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */ /* prototypes */ PROTOTYPEObj(net); @@ -114,5 +161,4 @@ PROTOTYPEObj(net); /* the name of our library binary */ #define LM_NET_FILENAME "lmnet" -#endif /* #ifdef SYSLOG_INET */ #endif /* #ifndef INCLUDED_NET_H */ diff --git a/runtime/netstrm.c b/runtime/netstrm.c new file mode 100644 index 00000000..2f4a1964 --- /dev/null +++ b/runtime/netstrm.c @@ -0,0 +1,353 @@ +/* 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 "net.h" +#include "module-template.h" +#include "obj.h" +#include "errmsg.h" +#include "netstrms.h" +#include "netstrm.h" + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +DEFobjCurrIf(netstrms) + + +/* Standard-Constructor */ +BEGINobjConstruct(netstrm) /* be sure to specify the object type also in END macro! */ +ENDobjConstruct(netstrm) + + +/* destructor for the netstrm object */ +BEGINobjDestruct(netstrm) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(netstrm) + if(pThis->pDrvrData != NULL) + iRet = pThis->Drvr.Destruct(&pThis->pDrvrData); +ENDobjDestruct(netstrm) + + +/* ConstructionFinalizer */ +static rsRetVal +netstrmConstructFinalize(netstrm_t *pThis) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + CHKiRet(pThis->Drvr.Construct(&pThis->pDrvrData)); +finalize_it: + RETiRet; +} + +/* abort a connection. This is much like Destruct(), but tries + * to discard any unsent data. -- rgerhards, 2008-03-24 + */ +static rsRetVal +AbortDestruct(netstrm_t **ppThis) +{ + DEFiRet; + assert(ppThis != NULL); + ISOBJ_TYPE_assert((*ppThis), netstrm); + + /* we do NOT exit on error, because that would make things worse */ + (*ppThis)->Drvr.Abort((*ppThis)->pDrvrData); + iRet = netstrmDestruct(ppThis); + + RETiRet; +} + + +/* accept an incoming connection request + * The netstrm instance that had the incoming request must be provided. If + * the connection request succeeds, a new netstrm object is created and + * passed back to the caller. The caller is responsible for destructing it. + * pReq is the nsd_t obj that has the accept request. + * rgerhards, 2008-04-21 + */ +static rsRetVal +AcceptConnReq(netstrm_t *pThis, netstrm_t **ppNew) +{ + nsd_t *pNewNsd = NULL; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, netstrm); + assert(ppNew != NULL); + + /* accept the new connection */ + CHKiRet(pThis->Drvr.AcceptConnReq(pThis->pDrvrData, &pNewNsd)); + /* construct our object so that we can use it... */ + CHKiRet(objUse(netstrms, DONT_LOAD_LIB)); /* use netstrms obj if not already done so */ + CHKiRet(netstrms.CreateStrm(pThis->pNS, ppNew)); + (*ppNew)->pDrvrData = pNewNsd; + +finalize_it: + if(iRet != RS_RET_OK) { + /* the close may be redundant, but that doesn't hurt... */ + if(pNewNsd != NULL) + pThis->Drvr.Destruct(&pNewNsd); + } + + RETiRet; +} + + +/* make the netstrm listen to specified port and IP. + * pLstnIP points to the port to listen to (NULL means "all"), + * iMaxSess has the maximum number of sessions permitted (this ist just a hint). + * pLstnPort must point to a port name or number. NULL is NOT permitted. + * rgerhards, 2008-04-22 + */ +static rsRetVal +LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*), + uchar *pLstnPort, uchar *pLstnIP, int iSessMax) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pNS, netstrms); + assert(fAddLstn != NULL); + assert(pLstnPort != NULL); + + CHKiRet(pNS->Drvr.LstnInit(pNS, pUsr, fAddLstn, pLstnPort, pLstnIP, iSessMax)); + +finalize_it: + RETiRet; +} + + +/* receive data from a tcp socket + * The lenBuf parameter must contain the max buffer size on entry and contains + * the number of octets read (or -1 in case of error) on exit. This function + * never blocks, not even when called on a blocking socket. That is important + * for client sockets, which are set to block during send, but should not + * block when trying to read data. If *pLenBuf is -1, an error occured and + * errno holds the exact error cause. + * rgerhards, 2008-03-17 + */ +static rsRetVal +Rcv(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.Rcv(pThis->pDrvrData, pBuf, pLenBuf); + RETiRet; +} + +/* here follows a number of methods that shuffle authentication settings down + * to the drivers. Drivers not supporting these settings may return an error + * state. + * -------------------------------------------------------------------------- */ + +/* set the driver mode + * rgerhards, 2008-04-28 + */ +static rsRetVal +SetDrvrMode(netstrm_t *pThis, int iMode) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.SetMode(pThis->pDrvrData, iMode); + RETiRet; +} + + +/* set the driver authentication mode -- rgerhards, 2008-05-16 + */ +static rsRetVal +SetDrvrAuthMode(netstrm_t *pThis, uchar *mode) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.SetAuthMode(pThis->pDrvrData, mode); + RETiRet; +} + + +/* set the driver's permitted peers -- rgerhards, 2008-05-19 */ +static rsRetVal +SetDrvrPermPeers(netstrm_t *pThis, permittedPeers_t *pPermPeers) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.SetPermPeers(pThis->pDrvrData, pPermPeers); + RETiRet; +} + + +/* End of methods to shuffle autentication settings to the driver. + * -------------------------------------------------------------------------- */ + + +/* send a buffer. On entry, pLenBuf contains the number of octets to + * write. On exit, it contains the number of octets actually written. + * If this number is lower than on entry, only a partial buffer has + * been written. + * rgerhards, 2008-03-19 + */ +static rsRetVal +Send(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.Send(pThis->pDrvrData, pBuf, pLenBuf); + RETiRet; +} + + +/* check connection - slim wrapper for NSD driver function */ +static void +CheckConnection(netstrm_t *pThis) +{ + ISOBJ_TYPE_assert(pThis, netstrm); + pThis->Drvr.CheckConnection(pThis->pDrvrData); +} + + +/* get remote hname - slim wrapper for NSD driver function */ +static rsRetVal +GetRemoteHName(netstrm_t *pThis, uchar **ppsz) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.GetRemoteHName(pThis->pDrvrData, ppsz); + RETiRet; +} + + +/* get remote IP - slim wrapper for NSD driver function */ +static rsRetVal +GetRemoteIP(netstrm_t *pThis, uchar **ppsz) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + iRet = pThis->Drvr.GetRemoteIP(pThis->pDrvrData, ppsz); + RETiRet; +} + + +/* open a connection to a remote host (server). + * rgerhards, 2008-03-19 + */ +static rsRetVal +Connect(netstrm_t *pThis, int family, uchar *port, uchar *host) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + assert(port != NULL); + assert(host != NULL); + iRet = pThis->Drvr.Connect(pThis->pDrvrData, family, port, host); + RETiRet; +} + + +/* Provide access to the underlying OS socket. This is dirty + * and scheduled to be removed. Does not work with all nsd drivers. + * See comment in netstrm interface for details. + * rgerhards, 2008-05-05 + */ +static rsRetVal +GetSock(netstrm_t *pThis, int *pSock) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrm); + assert(pSock != NULL); + iRet = pThis->Drvr.GetSock(pThis->pDrvrData, pSock); + RETiRet; +} + + +/* queryInterface function + */ +BEGINobjQueryInterface(netstrm) +CODESTARTobjQueryInterface(netstrm) + if(pIf->ifVersion != netstrmCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->Construct = netstrmConstruct; + pIf->ConstructFinalize = netstrmConstructFinalize; + pIf->Destruct = netstrmDestruct; + pIf->AbortDestruct = AbortDestruct; + pIf->Rcv = Rcv; + pIf->Send = Send; + pIf->Connect = Connect; + pIf->LstnInit = LstnInit; + pIf->AcceptConnReq = AcceptConnReq; + pIf->GetRemoteHName = GetRemoteHName; + pIf->GetRemoteIP = GetRemoteIP; + pIf->SetDrvrMode = SetDrvrMode; + pIf->SetDrvrAuthMode = SetDrvrAuthMode; + pIf->SetDrvrPermPeers = SetDrvrPermPeers; + pIf->CheckConnection = CheckConnection; + pIf->GetSock = GetSock; +finalize_it: +ENDobjQueryInterface(netstrm) + + +/* exit our class + */ +BEGINObjClassExit(netstrm, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(netstrm) + /* release objects we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + objRelease(netstrms, DONT_LOAD_LIB); +ENDObjClassExit(netstrm) + + +/* Initialize the netstrm class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-02-19 + */ +BEGINAbstractObjClassInit(netstrm, 1, OBJ_IS_CORE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + /* set our own handlers */ +ENDObjClassInit(netstrm) +/* vi:set ai: + */ diff --git a/runtime/netstrm.h b/runtime/netstrm.h new file mode 100644 index 00000000..1a97ef23 --- /dev/null +++ b/runtime/netstrm.h @@ -0,0 +1,73 @@ +/* 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 "netstrms.h" + +/* 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); + rsRetVal (*SetDrvrMode)(netstrm_t *pThis, int iMode); + rsRetVal (*SetDrvrAuthMode)(netstrm_t *pThis, uchar*); + rsRetVal (*SetDrvrPermPeers)(netstrm_t *pThis, permittedPeers_t*); + void (*CheckConnection)(netstrm_t *pThis); /* This is a trick mostly for plain tcp syslog */ + /* the GetSock() below is a hack to make imgssapi work. In the long term, + * we should migrate imgssapi to a stream driver, which will relieve us of + * this problem. Please note that nobody else should use GetSock(). Using it + * will also tie the caller to nsd_ptcp, because other drivers may not support + * it at all. Once the imgssapi problem is solved, GetSock should be removed from + * this interface. -- rgerhards, 2008-05-05 + */ + rsRetVal (*GetSock)(netstrm_t *pThis, int *pSock); +ENDinterface(netstrm) +#define netstrmCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ + +/* prototypes */ +PROTOTYPEObj(netstrm); + +/* the name of our library binary */ +#define LM_NETSTRM_FILENAME LM_NETSTRMS_FILENAME + +#endif /* #ifndef INCLUDED_NETSTRM_H */ diff --git a/runtime/netstrms.c b/runtime/netstrms.c new file mode 100644 index 00000000..2b754ecc --- /dev/null +++ b/runtime/netstrms.c @@ -0,0 +1,324 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "rsyslog.h" +#include "module-template.h" +#include "obj.h" +//#include "errmsg.h" +#include "nsd.h" +#include "netstrm.h" +#include "nssel.h" +#include "netstrms.h" + +MODULE_TYPE_LIB + +/* static data */ +DEFobjStaticHelpers +//DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) +DEFobjCurrIf(netstrm) + + +/* 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. + * WARNING: this code is mostly identical to similar code in + * nssel.c - TODO: abstract it and move it to some common place. + * rgerhards, 2008-04-18 + */ +static rsRetVal +loadDrvr(netstrms_t *pThis) +{ + DEFiRet; + uchar *pBaseDrvrName; + uchar szDrvrName[48]; /* 48 shall be large enough */ + + pBaseDrvrName = pThis->pBaseDrvrName; + if(pBaseDrvrName == NULL) /* if no drvr name is set, use system default */ + pBaseDrvrName = glbl.GetDfltNetstrmDrvr(); + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmnsd_%s", pBaseDrvrName) == sizeof(szDrvrName)) + ABORT_FINALIZE(RS_RET_DRVRNAME_TOO_LONG); + CHKmalloc(pThis->pDrvrName = (uchar*) strdup((char*)szDrvrName)); + + 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__, szDrvrName+2, szDrvrName, (void*) &pThis->Drvr)); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pThis->pDrvrName != NULL) + free(pThis->pDrvrName); + pThis->pDrvrName = NULL; + } + 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) + /* and now we must release our driver, if we got one. We use the presence of + * a driver name string as load indicator (because we also need that string + * to release the driver + */ + if(pThis->pDrvrName != NULL) { + obj.ReleaseObj(__FILE__, pThis->pDrvrName+2, pThis->pDrvrName, (void*) &pThis->Drvr); + free(pThis->pDrvrName); + } + if(pThis->pBaseDrvrName != NULL) { + free(pThis->pBaseDrvrName); + pThis->pBaseDrvrName = NULL; + } +ENDobjDestruct(netstrms) + + +/* ConstructionFinalizer */ +static rsRetVal +netstrmsConstructFinalize(netstrms_t *pThis) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrms); + CHKiRet(loadDrvr(pThis)); +finalize_it: + RETiRet; +} + + +/* set the base driver name. If the driver name + * is set to NULL, the previously set name is deleted but + * no name set again (which results in the system default being + * used)-- rgerhards, 2008-05-05 + */ +static rsRetVal +SetDrvrName(netstrms_t *pThis, uchar *pszName) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrms); + if(pThis->pBaseDrvrName != NULL) { + free(pThis->pBaseDrvrName); + pThis->pBaseDrvrName = NULL; + } + + if(pszName != NULL) { + CHKmalloc(pThis->pBaseDrvrName = (uchar*) strdup((char*) pszName)); + } +finalize_it: + RETiRet; +} + + +/* set the driver's permitted peers -- rgerhards, 2008-05-19 */ +static rsRetVal +SetDrvrPermPeers(netstrms_t *pThis, permittedPeers_t *pPermPeers) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrms); + pThis->pPermPeers = pPermPeers; + RETiRet; +} +/* return the driver's permitted peers + * We use non-standard calling conventions because it makes an awful lot + * of sense here. + * rgerhards, 2008-05-19 + */ +static permittedPeers_t* +GetDrvrPermPeers(netstrms_t *pThis) +{ + ISOBJ_TYPE_assert(pThis, netstrms); + return pThis->pPermPeers; +} + + +/* set the driver auth mode -- rgerhards, 2008-05-19 */ +static rsRetVal +SetDrvrAuthMode(netstrms_t *pThis, uchar *mode) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrms); + CHKmalloc(pThis->pszDrvrAuthMode = (uchar*)strdup((char*)mode)); +finalize_it: + RETiRet; +} +/* return the driver auth mode + * We use non-standard calling conventions because it makes an awful lot + * of sense here. + * rgerhards, 2008-05-19 + */ +static uchar* +GetDrvrAuthMode(netstrms_t *pThis) +{ + ISOBJ_TYPE_assert(pThis, netstrms); + return pThis->pszDrvrAuthMode; +} + + +/* set the driver mode -- rgerhards, 2008-04-30 */ +static rsRetVal +SetDrvrMode(netstrms_t *pThis, int iMode) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, netstrms); + pThis->iDrvrMode = iMode; + RETiRet; +} + + +/* return the driver mode + * We use non-standard calling conventions because it makes an awful lot + * of sense here. + * rgerhards, 2008-04-30 + */ +static int +GetDrvrMode(netstrms_t *pThis) +{ + ISOBJ_TYPE_assert(pThis, netstrms); + return pThis->iDrvrMode; +} + + +/* 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, DONT_LOAD_LIB)); + 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; + pIf->SetDrvrName = SetDrvrName; + pIf->SetDrvrMode = SetDrvrMode; + pIf->GetDrvrMode = GetDrvrMode; + pIf->SetDrvrAuthMode = SetDrvrAuthMode; + pIf->GetDrvrAuthMode = GetDrvrAuthMode; + pIf->SetDrvrPermPeers = SetDrvrPermPeers; + pIf->GetDrvrPermPeers = GetDrvrPermPeers; +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(glbl, CORE_COMPONENT); + objRelease(netstrm, DONT_LOAD_LIB); +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)); + + /* set our own handlers */ +ENDObjClassInit(netstrms) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + nsselClassExit(); + netstrmsClassExit(); + netstrmClassExit(); /* we use this object, so we must exit it after we are finished */ +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)); + CHKiRet(nsselClassInit(pModInfo)); + CHKiRet(netstrmsClassInit(pModInfo)); +ENDmodInit +/* vi:set ai: + */ diff --git a/runtime/netstrms.h b/runtime/netstrms.h new file mode 100644 index 00000000..3f686af6 --- /dev/null +++ b/runtime/netstrms.h @@ -0,0 +1,64 @@ +/* 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 *pBaseDrvrName; /**< nsd base driver name to use, or NULL if system default */ + uchar *pDrvrName; /**< full base driver name (set when driver is loaded) */ + int iDrvrMode; /**< current default driver mode */ + uchar *pszDrvrAuthMode; /**< current driver authentication mode */ + permittedPeers_t *pPermPeers;/**< current driver's permitted peers */ + + 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); + rsRetVal (*SetDrvrName)(netstrms_t *pThis, uchar *pszName); + rsRetVal (*SetDrvrMode)(netstrms_t *pThis, int iMode); + rsRetVal (*SetDrvrAuthMode)(netstrms_t *pThis, uchar*); + rsRetVal (*SetDrvrPermPeers)(netstrms_t *pThis, permittedPeers_t*); + int (*GetDrvrMode)(netstrms_t *pThis); + uchar* (*GetDrvrAuthMode)(netstrms_t *pThis); + permittedPeers_t* (*GetDrvrPermPeers)(netstrms_t *pThis); +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..1811f078 --- /dev/null +++ b/runtime/nsd.h @@ -0,0 +1,76 @@ +/* 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 (*SetMode)(nsd_t *pThis, int mode); /* sets a driver specific mode - see driver doc for details */ + rsRetVal (*SetAuthMode)(nsd_t *pThis, uchar*); /* sets a driver specific mode - see driver doc for details */ + rsRetVal (*SetPermPeers)(nsd_t *pThis, permittedPeers_t*); /* sets driver permitted peers for auth needs */ + void (*CheckConnection)(nsd_t *pThis); /* This is a trick mostly for plain tcp syslog */ + 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 3 /* 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..08623da8 --- /dev/null +++ b/runtime/nsd_gtls.c @@ -0,0 +1,1711 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#include <gcrypt.h> +#include <errno.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> + +#include "rsyslog.h" +#include "syslogd-types.h" +#include "module-template.h" +#include "cfsysline.h" +#include "obj.h" +#include "stringbuf.h" +#include "errmsg.h" +#include "net.h" +#include "nsd_ptcp.h" +#include "nsdsel_gtls.h" +#include "nsd_gtls.h" + +/* things to move to some better place/functionality - TODO */ +#define DH_BITS 1024 +#define CRLFILE "crl.pem" + + +GCRY_THREAD_OPTION_PTHREAD_IMPL; +MODULE_TYPE_LIB + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) +DEFobjCurrIf(net) +DEFobjCurrIf(nsd_ptcp) + +static int bGlblSrvrInitDone = 0; /**< 0 - server global init not yet done, 1 - already done */ + +static pthread_mutex_t mutGtlsStrerror; /**< a mutex protecting the potentially non-reentrant gtlStrerror() function */ + +/* a macro to check GnuTLS calls against unexpected errors */ +#define CHKgnutls(x) \ + if((gnuRet = (x)) != 0) { \ + uchar *pErr = gtlsStrerror(gnuRet); \ + dbgprintf("unexpected GnuTLS error %d in %s:%d: %s\n", gnuRet, __FILE__, __LINE__, pErr); \ + free(pErr); \ + ABORT_FINALIZE(RS_RET_GNUTLS_ERR); \ + } + + +/* ------------------------------ GnuTLS specifics ------------------------------ */ +static gnutls_certificate_credentials xcred; +static gnutls_dh_params dh_params; + +#ifdef DEBUG +/* This defines a log function to be provided to GnuTLS. It hopefully + * helps us track down hard to find problems. + * rgerhards, 2008-06-20 + */ +static void logFunction(int level, const char *msg) +{ + dbgprintf("GnuTLS log msg, level %d: %s\n", level, msg); +} +#endif /* #ifdef DEBUG */ + + +/* read in the whole content of a file. The caller is responsible for + * freeing the buffer. To prevent DOS, this function can NOT read + * files larger than 1MB (which still is *very* large). + * rgerhards, 2008-05-26 + */ +static rsRetVal +readFile(uchar *pszFile, gnutls_datum_t *pBuf) +{ + int fd; + struct stat stat_st; + DEFiRet; + + assert(pszFile != NULL); + assert(pBuf != NULL); + + pBuf->data = NULL; + + if((fd = open((char*)pszFile, 0)) == -1) { + errmsg.LogError(0, RS_RET_FILE_NOT_FOUND, "can not read file '%s'", pszFile); + ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); + + } + + if(fstat(fd, &stat_st) == -1) { + errmsg.LogError(0, RS_RET_FILE_NO_STAT, "can not stat file '%s'", pszFile); + ABORT_FINALIZE(RS_RET_FILE_NO_STAT); + } + + /* 1MB limit */ + if(stat_st.st_size > 1024 * 1024) { + errmsg.LogError(0, RS_RET_FILE_TOO_LARGE, "file '%s' too large, max 1MB", pszFile); + ABORT_FINALIZE(RS_RET_FILE_TOO_LARGE); + } + + CHKmalloc(pBuf->data = malloc(stat_st.st_size)); + pBuf->size = stat_st.st_size; + if(read(fd, pBuf->data, stat_st.st_size) != stat_st.st_size) { + errmsg.LogError(0, RS_RET_IO_ERROR, "error or incomplete read of file '%s'", pszFile); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + close(fd); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pBuf->data != NULL) { + free(pBuf->data); + pBuf->data = NULL; + pBuf->size = 0; + } + } + RETiRet; +} + + +/* Load the certificate and the private key into our own store. We need to do + * this in the client case, to support fingerprint authentication. In that case, + * we may be presented no matching root certificate, but we must provide ours. + * The only way to do that is via the cert callback interface, but for it we + * need to load certificates into our private store. + * rgerhards, 2008-05-26 + */ +static rsRetVal +gtlsLoadOurCertKey(nsd_gtls_t *pThis) +{ + DEFiRet; + int gnuRet; + gnutls_datum_t data = { NULL, 0 }; + uchar *keyFile; + uchar *certFile; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + certFile = glbl.GetDfltNetstrmDrvrCertFile(); + keyFile = glbl.GetDfltNetstrmDrvrKeyFile(); + + if(certFile == NULL || keyFile == NULL) { + /* in this case, we can not set our certificate. If we are + * a client and the server is running in "anon" auth mode, this + * may be well acceptable. In other cases, we will see some + * more error messages down the road. -- rgerhards, 2008-07-02 + */ + dbgprintf("our certificate is not set, file name values are cert: '%s', key: '%s'\n", + certFile, keyFile); + ABORT_FINALIZE(RS_RET_CERTLESS); + } + + /* try load certificate */ + CHKiRet(readFile(certFile, &data)); + CHKgnutls(gnutls_x509_crt_init(&pThis->ourCert)); + pThis->bOurCertIsInit = 1; + CHKgnutls(gnutls_x509_crt_import(pThis->ourCert, &data, GNUTLS_X509_FMT_PEM)); + free(data.data); + data.data = NULL; + + /* try load private key */ + CHKiRet(readFile(keyFile, &data)); + CHKgnutls(gnutls_x509_privkey_init(&pThis->ourKey)); + pThis->bOurKeyIsInit = 1; + CHKgnutls(gnutls_x509_privkey_import(pThis->ourKey, &data, GNUTLS_X509_FMT_PEM)); + free(data.data); + +finalize_it: + if(iRet != RS_RET_OK) { + if(data.data != NULL) + free(data.data); + if(pThis->bOurCertIsInit) + gnutls_x509_crt_deinit(pThis->ourCert); + if(pThis->bOurKeyIsInit) + gnutls_x509_privkey_deinit(pThis->ourKey); + } + RETiRet; +} + + +/* This callback must be associated with a session by calling + * gnutls_certificate_client_set_retrieve_function(session, cert_callback), + * before a handshake. We will always return the configured certificate, + * even if it does not match the peer's trusted CAs. This is necessary + * to use self-signed certs in fingerprint mode. And, yes, this usage + * of the callback is quite a hack. But it seems the only way to + * obey to the IETF -transport-tls I-D. + * Note: GnuTLS requires the function to return 0 on success and + * -1 on failure. + * rgerhards, 2008-05-27 + */ +static int +gtlsClientCertCallback(gnutls_session session, + __attribute__((unused)) const gnutls_datum* req_ca_rdn, int __attribute__((unused)) nreqs, + __attribute__((unused)) const gnutls_pk_algorithm* sign_algos, int __attribute__((unused)) sign_algos_length, + gnutls_retr_st *st) +{ + nsd_gtls_t *pThis; + + pThis = (nsd_gtls_t*) gnutls_session_get_ptr(session); + + st->type = GNUTLS_CRT_X509; + st->ncerts = 1; + st->cert.x509 = &pThis->ourCert; + st->key.x509 = pThis->ourKey; + st->deinit_all = 0; + + return 0; +} + + +/* This function extracts some information about this session's peer + * certificate. Works for X.509 certificates only. Adds all + * of the info to a cstr_t, which is handed over to the caller. + * Caller must destruct it when no longer needed. + * rgerhards, 2008-05-21 + */ +static rsRetVal +gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) +{ + char dn[128]; + uchar lnBuf[256]; + size_t size; + unsigned int algo, bits; + time_t expiration_time, activation_time; + const gnutls_datum *cert_list; + unsigned cert_list_size = 0; + gnutls_x509_crt cert; + cstr_t *pStr = NULL; + int gnuRet; + DEFiRet; + unsigned iAltName; + size_t szAltNameLen; + char szAltName[1024]; /* this is sufficient for the DNSNAME... */ + + assert(ppStr != NULL); + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + if(gnutls_certificate_type_get(pThis->sess) != GNUTLS_CRT_X509) + return RS_RET_TLS_CERT_ERR; + + cert_list = gnutls_certificate_get_peers(pThis->sess, &cert_list_size); + + CHKiRet(rsCStrConstruct(&pStr)); + + snprintf((char*)lnBuf, sizeof(lnBuf), "peer provided %d certificate(s). ", cert_list_size); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + if(cert_list_size > 0) { + /* we only print information about the first certificate */ + CHKgnutls(gnutls_x509_crt_init(&cert)); + CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)); + + CHKiRet(rsCStrAppendStr(pStr, (uchar*)"Certificate 1 info: ")); + + expiration_time = gnutls_x509_crt_get_expiration_time(cert); + activation_time = gnutls_x509_crt_get_activation_time(cert); + ctime_r(&activation_time, dn); + dn[strlen(dn) - 1] = '\0'; /* strip linefeed */ + snprintf((char*)lnBuf, sizeof(lnBuf), "certificate valid from %s ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + ctime_r(&expiration_time, dn); + dn[strlen(dn) - 1] = '\0'; /* strip linefeed */ + snprintf((char*)lnBuf, sizeof(lnBuf), "to %s; ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + /* Extract some of the public key algorithm's parameters */ + algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); + + snprintf((char*)lnBuf, sizeof(lnBuf), "Certificate public key: %s; ", + gnutls_pk_algorithm_get_name(algo)); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + /* names */ + size = sizeof(dn); + gnutls_x509_crt_get_dn(cert, dn, &size); + snprintf((char*)lnBuf, sizeof(lnBuf), "DN: %s; ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + size = sizeof(dn); + gnutls_x509_crt_get_issuer_dn(cert, dn, &size); + snprintf((char*)lnBuf, sizeof(lnBuf), "Issuer DN: %s; ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + /* dNSName alt name */ + iAltName = 0; + while(1) { /* loop broken below */ + szAltNameLen = sizeof(szAltName); + gnuRet = gnutls_x509_crt_get_subject_alt_name(cert, iAltName, + szAltName, &szAltNameLen, NULL); + if(gnuRet < 0) + break; + else if(gnuRet == GNUTLS_SAN_DNSNAME) { + /* we found it! */ + snprintf((char*)lnBuf, sizeof(lnBuf), "SAN:DNSname: %s; ", szAltName); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + /* do NOT break, because there may be multiple dNSName's! */ + } + ++iAltName; + } + + gnutls_x509_crt_deinit(cert); + } + + CHKiRet(rsCStrFinish(pStr)); + *ppStr = pStr; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pStr != NULL) + rsCStrDestruct(&pStr); + } + + RETiRet; +} + + + +#if 0 /* we may need this in the future - code needs to be looked at then! */ +/* This function will print some details of the + * given pThis->sess. + */ +static rsRetVal +print_info(nsd_gtls_t *pThis) +{ + const char *tmp; + gnutls_credentials_type cred; + gnutls_kx_algorithm kx; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + /* print the key exchange's algorithm name + */ + kx = gnutls_kx_get(pThis->sess); + tmp = gnutls_kx_get_name(kx); + dbgprintf("- Key Exchange: %s\n", tmp); + + /* Check the authentication type used and switch + * to the appropriate. + */ + cred = gnutls_auth_get_type(pThis->sess); + switch (cred) { + case GNUTLS_CRD_ANON: /* anonymous authentication */ + dbgprintf("- Anonymous DH using prime of %d bits\n", + gnutls_dh_get_prime_bits(pThis->sess)); + break; + case GNUTLS_CRD_CERTIFICATE: /* certificate authentication */ + /* Check if we have been using ephemeral Diffie Hellman. + */ + if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) { + dbgprintf("\n- Ephemeral DH using prime of %d bits\n", + gnutls_dh_get_prime_bits(pThis->sess)); + } + + /* if the certificate list is available, then + * print some information about it. + */ + gtlsPrintCert(pThis); + break; + case GNUTLS_CRD_SRP: /* certificate authentication */ + dbgprintf("GNUTLS_CRD_SRP/IA"); + break; + case GNUTLS_CRD_PSK: /* certificate authentication */ + dbgprintf("GNUTLS_CRD_PSK"); + break; + case GNUTLS_CRD_IA: /* certificate authentication */ + dbgprintf("GNUTLS_CRD_IA"); + break; + } /* switch */ + + /* print the protocol's name (ie TLS 1.0) */ + tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(pThis->sess)); + dbgprintf("- Protocol: %s\n", tmp); + + /* print the certificate type of the peer. + * ie X.509 + */ + tmp = gnutls_certificate_type_get_name( + gnutls_certificate_type_get(pThis->sess)); + + dbgprintf("- Certificate Type: %s\n", tmp); + + /* print the compression algorithm (if any) + */ + tmp = gnutls_compression_get_name( gnutls_compression_get(pThis->sess)); + dbgprintf("- Compression: %s\n", tmp); + + /* print the name of the cipher used. + * ie 3DES. + */ + tmp = gnutls_cipher_get_name(gnutls_cipher_get(pThis->sess)); + dbgprintf("- Cipher: %s\n", tmp); + + /* Print the MAC algorithms name. + * ie SHA1 + */ + tmp = gnutls_mac_get_name(gnutls_mac_get(pThis->sess)); + dbgprintf("- MAC: %s\n", tmp); + + RETiRet; +} +#endif + + +/* Convert a fingerprint to printable data. The conversion is carried out + * according IETF I-D syslog-transport-tls-12. The fingerprint string is + * returned in a new cstr object. It is the caller's responsibility to + * destruct that object. + * rgerhards, 2008-05-08 + */ +static rsRetVal +GenFingerprintStr(uchar *pFingerprint, size_t sizeFingerprint, cstr_t **ppStr) +{ + cstr_t *pStr = NULL; + uchar buf[4]; + size_t i; + DEFiRet; + + CHKiRet(rsCStrConstruct(&pStr)); + CHKiRet(rsCStrAppendStrWithLen(pStr, (uchar*)"SHA1", 4)); + for(i = 0 ; i < sizeFingerprint ; ++i) { + snprintf((char*)buf, sizeof(buf), ":%2.2X", pFingerprint[i]); + CHKiRet(rsCStrAppendStrWithLen(pStr, buf, 3)); + } + CHKiRet(rsCStrFinish(pStr)); + + *ppStr = pStr; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pStr != NULL) + rsCStrDestruct(&pStr); + } + RETiRet; +} + + +/* a thread-safe variant of gnutls_strerror + * The caller must free the returned string. + * rgerhards, 2008-04-30 + */ +uchar *gtlsStrerror(int error) +{ + uchar *pErr; + + pthread_mutex_lock(&mutGtlsStrerror); + pErr = (uchar*) strdup(gnutls_strerror(error)); + pthread_mutex_unlock(&mutGtlsStrerror); + + return pErr; +} + + +/* try to receive a record from the remote peer. This works with + * our own abstraction and handles local buffering and EAGAIN. + * See details on local buffering in Rcv(9 header-comment. + * This function MUST only be called when the local buffer is + * empty. Calling it otherwise will cause losss of current buffer + * data. + * rgerhards, 2008-06-24 + */ +rsRetVal +gtlsRecordRecv(nsd_gtls_t *pThis) +{ + ssize_t lenRcvd; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + lenRcvd = gnutls_record_recv(pThis->sess, pThis->pszRcvBuf, NSD_GTLS_MAX_RCVBUF); + if(lenRcvd >= 0) { + pThis->lenRcvBuf = lenRcvd; + pThis->ptrRcvBuf = 0; + } else if(lenRcvd == GNUTLS_E_AGAIN || lenRcvd == GNUTLS_E_INTERRUPTED) { + pThis->rtryCall = gtlsRtry_recv; + dbgprintf("GnuTLS receive requires a retry (this most probably is OK and no error condition)\n"); + ABORT_FINALIZE(RS_RET_RETRY); + } else { + int gnuRet; /* TODO: build a specific function for GnuTLS error reporting */ + CHKgnutls(lenRcvd); /* this will abort the function */ + } + +finalize_it: + dbgprintf("gtlsRecordRecv return. nsd %p, iRet %d, lenRcvd %d, lenRcvBuf %d, ptrRcvBuf %d\n", pThis, iRet, (int) lenRcvd, pThis->lenRcvBuf, pThis->ptrRcvBuf); + RETiRet; +} + + +/* add our own certificate to the certificate set, so that the peer + * can identify us. Please note that we try to use mutual authentication, + * so we always add a cert, even if we are in the client role (later, + * this may be controlled by a config setting). + * rgerhards, 2008-05-15 + */ +static rsRetVal +gtlsAddOurCert(void) +{ + int gnuRet; + uchar *keyFile; + uchar *certFile; + uchar *pGnuErr; /* for GnuTLS error reporting */ + DEFiRet; + + certFile = glbl.GetDfltNetstrmDrvrCertFile(); + keyFile = glbl.GetDfltNetstrmDrvrKeyFile(); + dbgprintf("GTLS certificate file: '%s'\n", certFile); + dbgprintf("GTLS key file: '%s'\n", keyFile); + CHKgnutls(gnutls_certificate_set_x509_key_file(xcred, (char*)certFile, (char*)keyFile, GNUTLS_X509_FMT_PEM)); + +finalize_it: + if(iRet != RS_RET_OK) { + pGnuErr = gtlsStrerror(gnuRet); + errno = 0; + errmsg.LogError(0, iRet, "error adding our certificate. GnuTLS error %d, message: '%s', " + "key: '%s', cert: '%s'", gnuRet, pGnuErr, keyFile, certFile); + free(pGnuErr); + } + RETiRet; +} + + +/* globally initialize GnuTLS */ +static rsRetVal +gtlsGlblInit(void) +{ + int gnuRet; + uchar *cafile; + DEFiRet; + + /* gcry_control must be called first, so that the thread system is correctly set up */ + gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + CHKgnutls(gnutls_global_init()); + + /* X509 stuff */ + CHKgnutls(gnutls_certificate_allocate_credentials(&xcred)); + + /* sets the trusted cas file */ + cafile = glbl.GetDfltNetstrmDrvrCAF(); + dbgprintf("GTLS CA file: '%s'\n", cafile); + gnuRet = gnutls_certificate_set_x509_trust_file(xcred, (char*)cafile, GNUTLS_X509_FMT_PEM); + if(gnuRet < 0) { + /* TODO; a more generic error-tracking function (this one based on CHKgnutls()) */ + uchar *pErr = gtlsStrerror(gnuRet); + dbgprintf("unexpected GnuTLS error %d in %s:%d: %s\n", gnuRet, __FILE__, __LINE__, pErr); + free(pErr); + ABORT_FINALIZE(RS_RET_GNUTLS_ERR); + } + +# ifdef DEBUG +#if 0 /* do this in special cases only. WARNING: if active, it may reveal sensitive information! */ + /* intialize log function - set a level only for hard-to-find bugs */ + gnutls_global_set_log_function(logFunction); + gnutls_global_set_log_level(10); /* 0 (no) to 9 (most), 10 everything */ +# endif +# endif + +finalize_it: + RETiRet; +} + +static rsRetVal +gtlsInitSession(nsd_gtls_t *pThis) +{ + DEFiRet; + int gnuRet; + gnutls_session session; + + gnutls_init(&session, GNUTLS_SERVER); + pThis->bHaveSess = 1; + pThis->bIsInitiator = 0; + + /* avoid calling all the priority functions, since the defaults are adequate. */ + CHKgnutls(gnutls_set_default_priority(session)); + CHKgnutls(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred)); + + /* request client certificate if any. */ + gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUEST); + gnutls_dh_set_prime_bits(session, DH_BITS); + + pThis->sess = session; + +finalize_it: + RETiRet; +} + + +static rsRetVal +generate_dh_params(void) +{ + int gnuRet; + DEFiRet; + /* Generate Diffie Hellman parameters - for use with DHE + * kx algorithms. These should be discarded and regenerated + * once a day, once a week or once a month. Depending on the + * security requirements. + */ + CHKgnutls(gnutls_dh_params_init( &dh_params)); + CHKgnutls(gnutls_dh_params_generate2( dh_params, DH_BITS)); +finalize_it: + RETiRet; +} + + +/* set up all global things that are needed for server operations + * rgerhards, 2008-04-30 + */ +static rsRetVal +gtlsGlblInitLstn(void) +{ + DEFiRet; + + if(bGlblSrvrInitDone == 0) { + /* we do not use CRLs right now, and I doubt we'll ever do. This functionality is + * considered legacy. -- rgerhards, 2008-05-05 + */ + /*CHKgnutls(gnutls_certificate_set_x509_crl_file(xcred, CRLFILE, GNUTLS_X509_FMT_PEM));*/ + CHKiRet(generate_dh_params()); + gnutls_certificate_set_dh_params(xcred, dh_params); /* this is void */ + bGlblSrvrInitDone = 1; /* we are all set now */ + + /* now we need to add our certificate */ + CHKiRet(gtlsAddOurCert()); + } + +finalize_it: + RETiRet; +} + + +/* Obtain the CN from the DN field and hand it back to the caller + * (which is responsible for destructing it). We try to follow + * RFC2253 as far as it makes sense for our use-case. This function + * is considered a compromise providing good-enough correctness while + * limiting code size and complexity. If a problem occurs, we may enhance + * this function. A (pointer to a) certificate must be caller-provided. + * If no CN is contained in the cert, no string is returned + * (*ppstrCN remains NULL). *ppstrCN MUST be NULL on entry! + * rgerhards, 2008-05-22 + */ +static rsRetVal +gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN) +{ + DEFiRet; + int gnuRet; + int i; + int bFound; + cstr_t *pstrCN = NULL; + size_t size; + /* big var the last, so we hope to have all we usually neeed within one mem cache line */ + uchar szDN[1024]; /* this should really be large enough for any non-malicious case... */ + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + assert(pCert != NULL); + assert(ppstrCN != NULL); + assert(*ppstrCN == NULL); + + size = sizeof(szDN); + CHKgnutls(gnutls_x509_crt_get_dn(*pCert, (char*)szDN, &size)); + + /* now search for the CN part */ + i = 0; + bFound = 0; + while(!bFound && szDN[i] != '\0') { + /* note that we do not overrun our string due to boolean shortcut + * operations. If we have '\0', the if does not match and evaluation + * stops. Order of checks is obviously important! + */ + if(szDN[i] == 'C' && szDN[i+1] == 'N' && szDN[i+2] == '=') { + bFound = 1; + i += 2; + } + i++; + + } + + if(!bFound) { + FINALIZE; /* we are done */ + } + + /* we found a common name, now extract it */ + CHKiRet(rsCStrConstruct(&pstrCN)); + while(szDN[i] != '\0' && szDN[i] != ',') { + if(szDN[i] == '\\') { + /* hex escapes are not implemented */ + ++i; /* escape char processed */ + if(szDN[i] == '\0') + ABORT_FINALIZE(RS_RET_CERT_INVALID_DN); + CHKiRet(rsCStrAppendChar(pstrCN, szDN[i])); + } else { + CHKiRet(rsCStrAppendChar(pstrCN, szDN[i])); + } + ++i; /* char processed */ + } + CHKiRet(rsCStrFinish(pstrCN)); + + /* we got it - we ignore the rest of the DN string (if any). So we may + * not detect if it contains more than one CN + */ + + *ppstrCN = pstrCN; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pstrCN != NULL) + rsCStrDestruct(&pstrCN); + } + + RETiRet; +} + + +/* Check the peer's ID in fingerprint auth mode. + * rgerhards, 2008-05-22 + */ +static rsRetVal +gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert) +{ + uchar fingerprint[20]; + size_t size; + cstr_t *pstrFingerprint = NULL; + int bFoundPositiveMatch; + permittedPeers_t *pPeer; + int gnuRet; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + /* obtain the SHA1 fingerprint */ + size = sizeof(fingerprint); + CHKgnutls(gnutls_x509_crt_get_fingerprint(*pCert, GNUTLS_DIG_SHA1, fingerprint, &size)); + CHKiRet(GenFingerprintStr(fingerprint, size, &pstrFingerprint)); + dbgprintf("peer's certificate SHA1 fingerprint: %s\n", rsCStrGetSzStr(pstrFingerprint)); + + /* now search through the permitted peers to see if we can find a permitted one */ + bFoundPositiveMatch = 0; + pPeer = pThis->pPermPeers; + while(pPeer != NULL && !bFoundPositiveMatch) { + if(!rsCStrSzStrCmp(pstrFingerprint, pPeer->pszID, strlen((char*) pPeer->pszID))) { + bFoundPositiveMatch = 1; + } else { + pPeer = pPeer->pNext; + } + } + + if(!bFoundPositiveMatch) { + dbgprintf("invalid peer fingerprint, not permitted to talk to it\n"); + if(pThis->bReportAuthErr == 1) { + errno = 0; + errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer fingerprint '%s' unknown - we are " + "not permitted to talk to it", rsCStrGetSzStr(pstrFingerprint)); + pThis->bReportAuthErr = 0; + } + ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT); + } + +finalize_it: + if(pstrFingerprint != NULL) + rsCStrDestruct(&pstrFingerprint); + RETiRet; +} + + +/* Perform a match on ONE peer name obtained from the certificate. This name + * is checked against the set of configured credentials. *pbFoundPositiveMatch is + * set to 1 if the ID matches. *pbFoundPositiveMatch must have been initialized + * to 0 by the caller (this is a performance enhancement as we expect to be + * called multiple times). + * TODO: implemet wildcards? + * rgerhards, 2008-05-26 + */ +static rsRetVal +gtlsChkOnePeerName(nsd_gtls_t *pThis, uchar *pszPeerID, int *pbFoundPositiveMatch) +{ + permittedPeers_t *pPeer; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + assert(pszPeerID != NULL); + assert(pbFoundPositiveMatch != NULL); + + if(pThis->pPermPeers) { /* do we have configured peer IDs? */ + pPeer = pThis->pPermPeers; + while(pPeer != NULL) { + CHKiRet(net.PermittedPeerWildcardMatch(pPeer, pszPeerID, pbFoundPositiveMatch)); + if(*pbFoundPositiveMatch) + break; + pPeer = pPeer->pNext; + } + } else { + /* we do not have configured peer IDs, so we use defaults */ + if( pThis->pszConnectHost + && !strcmp((char*)pszPeerID, (char*)pThis->pszConnectHost)) { + *pbFoundPositiveMatch = 1; + } + } + +finalize_it: + RETiRet; +} + + +/* Check the peer's ID in name auth mode. + * rgerhards, 2008-05-22 + */ +static rsRetVal +gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert) +{ + uchar lnBuf[256]; + char szAltName[1024]; /* this is sufficient for the DNSNAME... */ + int iAltName; + size_t szAltNameLen; + int bFoundPositiveMatch; + cstr_t *pStr = NULL; + cstr_t *pstrCN = NULL; + int gnuRet; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + bFoundPositiveMatch = 0; + CHKiRet(rsCStrConstruct(&pStr)); + + /* first search through the dNSName subject alt names */ + iAltName = 0; + while(!bFoundPositiveMatch) { /* loop broken below */ + szAltNameLen = sizeof(szAltName); + gnuRet = gnutls_x509_crt_get_subject_alt_name(*pCert, iAltName, + szAltName, &szAltNameLen, NULL); + if(gnuRet < 0) + break; + else if(gnuRet == GNUTLS_SAN_DNSNAME) { + dbgprintf("subject alt dnsName: '%s'\n", szAltName); + snprintf((char*)lnBuf, sizeof(lnBuf), "DNSname: %s; ", szAltName); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(gtlsChkOnePeerName(pThis, (uchar*)szAltName, &bFoundPositiveMatch)); + /* do NOT break, because there may be multiple dNSName's! */ + } + ++iAltName; + } + + if(!bFoundPositiveMatch) { + /* if we did not succeed so far, we try the CN part of the DN... */ + CHKiRet(gtlsGetCN(pThis, pCert, &pstrCN)); + if(pstrCN != NULL) { /* NULL if there was no CN present */ + dbgprintf("gtls now checking auth for CN '%s'\n", rsCStrGetSzStr(pstrCN)); + snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", rsCStrGetSzStr(pstrCN)); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(gtlsChkOnePeerName(pThis, rsCStrGetSzStr(pstrCN), &bFoundPositiveMatch)); + } + } + + if(!bFoundPositiveMatch) { + dbgprintf("invalid peer name, not permitted to talk to it\n"); + if(pThis->bReportAuthErr == 1) { + CHKiRet(rsCStrFinish(pStr)); + errno = 0; + errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer name not authorized - " + "not permitted to talk to it. Names: %s", + rsCStrGetSzStr(pStr)); + pThis->bReportAuthErr = 0; + } + ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT); + } + +finalize_it: + if(pStr != NULL) + rsCStrDestruct(&pStr); + if(pstrCN != NULL) + rsCStrDestruct(&pstrCN); + RETiRet; +} + + +/* check the ID of the remote peer - used for both fingerprint and + * name authentication. This is common code. Will call into specific + * drivers once the certificate has been obtained. + * rgerhards, 2008-05-08 + */ +static rsRetVal +gtlsChkPeerID(nsd_gtls_t *pThis) +{ + const gnutls_datum *cert_list; + unsigned int list_size = 0; + gnutls_x509_crt cert; + int bMustDeinitCert = 0; + int gnuRet; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + /* This function only works for X.509 certificates. */ + if(gnutls_certificate_type_get(pThis->sess) != GNUTLS_CRT_X509) + return RS_RET_TLS_CERT_ERR; + + cert_list = gnutls_certificate_get_peers(pThis->sess, &list_size); + + if(list_size < 1) { + if(pThis->bReportAuthErr == 1) { + errno = 0; + errmsg.LogError(0, RS_RET_TLS_NO_CERT, "error: peer did not provide a certificate, " + "not permitted to talk to it"); + pThis->bReportAuthErr = 0; + } + ABORT_FINALIZE(RS_RET_TLS_NO_CERT); + } + + /* If we reach this point, we have at least one valid certificate. + * We always use only the first certificate. As of GnuTLS documentation, the + * first certificate always contains the remote peer's own certificate. All other + * certificates are issuer's certificates (up the chain). We are only interested + * in the first certificate, which is our peer. -- rgerhards, 2008-05-08 + */ + CHKgnutls(gnutls_x509_crt_init(&cert)); + bMustDeinitCert = 1; /* indicate cert is initialized and must be freed on exit */ + CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)); + + /* Now we see which actual authentication code we must call. */ + if(pThis->authMode == GTLS_AUTH_CERTFINGERPRINT) { + CHKiRet(gtlsChkPeerFingerprint(pThis, &cert)); + } else { + assert(pThis->authMode == GTLS_AUTH_CERTNAME); + CHKiRet(gtlsChkPeerName(pThis, &cert)); + } + +finalize_it: + if(bMustDeinitCert) + gnutls_x509_crt_deinit(cert); + + RETiRet; +} + + +/* Verify the validity of the remote peer's certificate. + * rgerhards, 2008-05-21 + */ +static rsRetVal +gtlsChkPeerCertValidity(nsd_gtls_t *pThis) +{ + DEFiRet; + char *pszErrCause; + int gnuRet; + cstr_t *pStr; + unsigned stateCert; + const gnutls_datum *cert_list; + unsigned cert_list_size = 0; + gnutls_x509_crt cert; + unsigned i; + time_t ttCert; + time_t ttNow; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + /* check if we have at least one cert */ + cert_list = gnutls_certificate_get_peers(pThis->sess, &cert_list_size); + if(cert_list_size < 1) { + errno = 0; + errmsg.LogError(0, RS_RET_TLS_NO_CERT, "peer did not provide a certificate, not permitted to talk to it"); + ABORT_FINALIZE(RS_RET_TLS_NO_CERT); + } + + CHKgnutls(gnutls_certificate_verify_peers2(pThis->sess, &stateCert)); + + if(stateCert & GNUTLS_CERT_INVALID) { + /* provide error details if we have them */ + if(stateCert & GNUTLS_CERT_SIGNER_NOT_FOUND) { + pszErrCause = "signer not found"; + } else if(stateCert & GNUTLS_CERT_SIGNER_NOT_CA) { + pszErrCause = "signer is not a CA"; + } else if(stateCert & GNUTLS_CERT_INSECURE_ALGORITHM) { + pszErrCause = "insecure algorithm"; + } else if(stateCert & GNUTLS_CERT_REVOKED) { + pszErrCause = "certificate revoked"; + } else { + pszErrCause = "GnuTLS returned no specific reason"; + dbgprintf("GnuTLS returned no specific reason for GNUTLS_CERT_INVALID, certificate " + "status is %d\n", stateCert); + } + errmsg.LogError(0, NO_ERRCODE, "not permitted to talk to peer, certificate invalid: %s", + pszErrCause); + gtlsGetCertInfo(pThis, &pStr); + errmsg.LogError(0, NO_ERRCODE, "invalid cert info: %s", rsCStrGetSzStr(pStr)); + rsCStrDestruct(&pStr); + ABORT_FINALIZE(RS_RET_CERT_INVALID); + } + + /* get current time for certificate validation */ + if(time(&ttNow) == -1) + ABORT_FINALIZE(RS_RET_SYS_ERR); + + /* as it looks, we need to validate the expiration dates ourselves... + * We need to loop through all certificates as we need to make sure the + * interim certificates are also not expired. + */ + for(i = 0 ; i < cert_list_size ; ++i) { + CHKgnutls(gnutls_x509_crt_init(&cert)); + CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER)); + ttCert = gnutls_x509_crt_get_activation_time(cert); + if(ttCert == -1) + ABORT_FINALIZE(RS_RET_TLS_CERT_ERR); + else if(ttCert > ttNow) { + errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "not permitted to talk to peer: certificate %d not yet active", i); + gtlsGetCertInfo(pThis, &pStr); + errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "invalid cert info: %s", rsCStrGetSzStr(pStr)); + rsCStrDestruct(&pStr); + ABORT_FINALIZE(RS_RET_CERT_NOT_YET_ACTIVE); + } + + ttCert = gnutls_x509_crt_get_expiration_time(cert); + if(ttCert == -1) + ABORT_FINALIZE(RS_RET_TLS_CERT_ERR); + else if(ttCert < ttNow) { + errmsg.LogError(0, RS_RET_CERT_EXPIRED, "not permitted to talk to peer: certificate %d expired", i); + gtlsGetCertInfo(pThis, &pStr); + errmsg.LogError(0, RS_RET_CERT_EXPIRED, "invalid cert info: %s", rsCStrGetSzStr(pStr)); + rsCStrDestruct(&pStr); + ABORT_FINALIZE(RS_RET_CERT_EXPIRED); + } + gnutls_x509_crt_deinit(cert); + } + +finalize_it: + RETiRet; +} + + +/* check if it is OK to talk to the remote peer + * rgerhards, 2008-05-21 + */ +rsRetVal +gtlsChkPeerAuth(nsd_gtls_t *pThis) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + /* call the actual function based on current auth mode */ + switch(pThis->authMode) { + case GTLS_AUTH_CERTNAME: + /* if we check the name, we must ensure the cert is valid */ + CHKiRet(gtlsChkPeerCertValidity(pThis)); + CHKiRet(gtlsChkPeerID(pThis)); + break; + case GTLS_AUTH_CERTFINGERPRINT: + CHKiRet(gtlsChkPeerID(pThis)); + break; + case GTLS_AUTH_CERTVALID: + CHKiRet(gtlsChkPeerCertValidity(pThis)); + break; + case GTLS_AUTH_CERTANON: + FINALIZE; + break; + } + +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) { + if(pThis->bIsInitiator) { + 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; +} + + +/* a small wrapper for gnutls_transport_set_ptr(). The main intension for + * creating this wrapper is to get the annoying "cast to pointer from different + * size" compiler warning just once. There seems to be no way around it, see: + * http://lists.gnu.org/archive/html/help-gnutls/2008-05/msg00000.html + * rgerhards, 2008.05-07 + */ +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +static inline void +gtlsSetTransportPtr(nsd_gtls_t *pThis, int sock) +{ + /* Note: the compiler warning for the next line is OK - see header comment! */ + gnutls_transport_set_ptr(pThis->sess, (gnutls_transport_ptr_t) sock); +} +#pragma GCC diagnostic warning "-Wint-to-pointer-cast" + +/* ---------------------------- 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->bReportAuthErr = 1; +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); + } + + if(pThis->pszConnectHost != NULL) { + free(pThis->pszConnectHost); + } + + if(pThis->pszRcvBuf == NULL) { + free(pThis->pszRcvBuf); + } + + if(pThis->bOurCertIsInit) + gnutls_x509_crt_deinit(pThis->ourCert); + if(pThis->bOurKeyIsInit) + gnutls_x509_privkey_deinit(pThis->ourKey); +ENDobjDestruct(nsd_gtls) + + +/* Set the driver mode. For us, this has the following meaning: + * 0 - work in plain tcp mode, without tls (e.g. before a STARTTLS) + * 1 - work in TLS mode + * rgerhards, 2008-04-28 + */ +static rsRetVal +SetMode(nsd_t *pNsd, int mode) +{ + DEFiRet; + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + + ISOBJ_TYPE_assert((pThis), nsd_gtls); + if(mode != 0 && mode != 1) { + errmsg.LogError(0, RS_RET_INVALID_DRVR_MODE, "error: driver mode %d not supported by " + "gtls netstream driver", mode); + ABORT_FINALIZE(RS_RET_INVALID_DRVR_MODE); + } + + pThis->iMode = mode; + +finalize_it: + RETiRet; +} + + +/* Set the authentication mode. For us, the following is supported: + * anon - no certificate checks whatsoever (discouraged, but supported) + * x509/certvalid - (just) check certificate validity + * x509/fingerprint - certificate fingerprint + * x509/name - cerfificate name check + * mode == NULL is valid and defaults to x509/name + * rgerhards, 2008-05-16 + */ +static rsRetVal +SetAuthMode(nsd_t *pNsd, uchar *mode) +{ + DEFiRet; + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + + ISOBJ_TYPE_assert((pThis), nsd_gtls); + if(mode == NULL || !strcasecmp((char*)mode, "x509/name")) { + pThis->authMode = GTLS_AUTH_CERTNAME; + } else if(!strcasecmp((char*) mode, "x509/fingerprint")) { + pThis->authMode = GTLS_AUTH_CERTFINGERPRINT; + } else if(!strcasecmp((char*) mode, "x509/certvalid")) { + pThis->authMode = GTLS_AUTH_CERTVALID; + } else if(!strcasecmp((char*) mode, "anon")) { + pThis->authMode = GTLS_AUTH_CERTANON; + } else { + errmsg.LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: authentication mode '%s' not supported by " + "gtls netstream driver", mode); + ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED); + } + +/* TODO: clear stored IDs! */ + +finalize_it: +dbgprintf("gtls auth mode %d set\n", pThis->authMode); + RETiRet; +} + + +/* Set permitted peers. It is depending on the auth mode if this are + * fingerprints or names. -- rgerhards, 2008-05-19 + */ +static rsRetVal +SetPermPeers(nsd_t *pNsd, permittedPeers_t *pPermPeers) +{ + DEFiRet; + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + + ISOBJ_TYPE_assert((pThis), nsd_gtls); + if(pPermPeers == NULL) + FINALIZE; + + if(pThis->authMode != GTLS_AUTH_CERTFINGERPRINT && pThis->authMode != GTLS_AUTH_CERTNAME) { + errmsg.LogError(0, RS_RET_VALUE_NOT_IN_THIS_MODE, "authentication not supported by " + "gtls netstream driver in the configured authentication mode - ignored"); + ABORT_FINALIZE(RS_RET_VALUE_NOT_IN_THIS_MODE); + } + + pThis->pPermPeers = pPermPeers; + +finalize_it: + 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. -- 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; + CHKiRet(gtlsGlblInitLstn()); + iRet = nsd_ptcp.LstnInit(pNS, pUsr, fAddLstn, pLstnPort, pLstnIP, iSessMax); +finalize_it: + RETiRet; +} + + +/* This function checks if the connection is still alive - well, kind of... + * This is a dummy here. For details, check function common in ptcp driver. + * rgerhards, 2008-06-09 + */ +static void +CheckConnection(nsd_t __attribute__((unused)) *pNsd) +{ + /* dummy, do nothing */ +} + + +/* 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; + int gnuRet; + nsd_gtls_t *pNew = NULL; + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + + ISOBJ_TYPE_assert((pThis), nsd_gtls); + CHKiRet(nsd_gtlsConstruct(&pNew)); // TODO: prevent construct/destruct! + CHKiRet(nsd_ptcp.Destruct(&pNew->pTcp)); + CHKiRet(nsd_ptcp.AcceptConnReq(pThis->pTcp, &pNew->pTcp)); + + if(pThis->iMode == 0) { + /* we are in non-TLS mode, so we are done */ + *ppNew = (nsd_t*) pNew; + FINALIZE; + } + + /* if we reach this point, we are in TLS mode */ + CHKiRet(gtlsInitSession(pNew)); + gtlsSetTransportPtr(pNew, ((nsd_ptcp_t*) (pNew->pTcp))->sock); + pNew->authMode = pThis->authMode; + pNew->pPermPeers = pThis->pPermPeers; + + /* we now do the handshake. This is a bit complicated, because we are + * on non-blocking sockets. Usually, the handshake will not complete + * immediately, so that we need to retry it some time later. + */ + gnuRet = gnutls_handshake(pNew->sess); + if(gnuRet == GNUTLS_E_AGAIN || gnuRet == GNUTLS_E_INTERRUPTED) { + pNew->rtryCall = gtlsRtry_handshake; + dbgprintf("GnuTLS handshake does not complete immediately - setting to retry (this is OK and normal)\n"); + } else if(gnuRet == 0) { + /* we got a handshake, now check authorization */ + CHKiRet(gtlsChkPeerAuth(pNew)); + } else { + ABORT_FINALIZE(RS_RET_TLS_HANDSHAKE_ERR); + } + + pNew->iMode = 1; /* this session is now in TLS mode! */ + + *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 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. -- rgerhards, 2008-03-17 + * The function now follows the usual iRet calling sequence. + * With GnuTLS, we may need to restart a recv() system call. If so, we need + * to supply the SAME buffer on the retry. We can not assure this, as the + * caller is free to call us with any buffer location (and in current + * implementation, it is on the stack and extremely likely to change). To + * work-around this problem, we allocate a buffer ourselfs and always receive + * into that buffer. We pass data on to the caller only after we have received it. + * To save some space, we allocate that internal buffer only when it is actually + * needed, which means when we reach this function for the first time. To keep + * the algorithm simple, we always supply data only from the internal buffer, + * even if it is a single byte. As we have a stream, the caller must be prepared + * to accept messages in any order, so we do not need to take care about this. + * Please note that the logic also forces us to do some "faking" in select(), as + * we must provide a fake "is ready for readign" status if we have data inside our + * buffer. -- rgerhards, 2008-06-23 + */ +static rsRetVal +Rcv(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf) +{ + DEFiRet; + ssize_t iBytesCopy; /* how many bytes are to be copied to the client buffer? */ + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + if(pThis->bAbortConn) + ABORT_FINALIZE(RS_RET_CONNECTION_ABORTREQ); + + if(pThis->iMode == 0) { + CHKiRet(nsd_ptcp.Rcv(pThis->pTcp, pBuf, pLenBuf)); + FINALIZE; + } + + /* --- in TLS mode now --- */ + + /* Buffer logic applies only if we are in TLS mode. Here we + * assume that we will switch from plain to TLS, but never back. This + * assumption may be unsafe, but it is the model for the time being and I + * do not see any valid reason why we should switch back to plain TCP after + * we were in TLS mode. However, in that case we may lose something that + * is already in the receive buffer ... risk accepted. -- rgerhards, 2008-06-23 + */ + + if(pThis->pszRcvBuf == NULL) { + /* we have no buffer, so we need to malloc one */ + CHKmalloc(pThis->pszRcvBuf = malloc(NSD_GTLS_MAX_RCVBUF)); + pThis->lenRcvBuf = -1; + } + + /* now check if we have something in our buffer. If so, we satisfy + * the request from buffer contents. + */ + if(pThis->lenRcvBuf == -1) { /* no data present, must read */ + CHKiRet(gtlsRecordRecv(pThis)); + } + + if(pThis->lenRcvBuf == 0) { /* EOS */ + *pLenBuf = 0; + ABORT_FINALIZE(RS_RET_CLOSED); + } + + /* if we reach this point, data is present in the buffer and must be copied */ + iBytesCopy = pThis->lenRcvBuf - pThis->ptrRcvBuf; + if(iBytesCopy > *pLenBuf) { + iBytesCopy = *pLenBuf; + } else { + pThis->lenRcvBuf = -1; /* buffer will be emptied below */ + } + + memcpy(pBuf, pThis->pszRcvBuf + pThis->ptrRcvBuf, iBytesCopy); + pThis->ptrRcvBuf += iBytesCopy; + *pLenBuf = iBytesCopy; + +finalize_it: + dbgprintf("gtlsRcv return. nsd %p, iRet %d, lenRcvBuf %d, ptrRcvBuf %d\n", pThis, iRet, pThis->lenRcvBuf, pThis->ptrRcvBuf); + 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->bAbortConn) + ABORT_FINALIZE(RS_RET_CONNECTION_ABORTREQ); + + 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) { + dbgprintf("unexpected GnuTLS error %d in %s:%d\n", iSent, __FILE__, __LINE__); + gnutls_perror(iSent); /* TODO: can we do better? */ + 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; + /* TODO: later? static const int cert_type_priority[3] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };*/ + static const int cert_type_priority[2] = { GNUTLS_CRT_X509, 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; + pThis->bIsInitiator = 1; + + /* in the client case, we need to set a callback that ensures our certificate + * will be presented to the server even if it is not signed by one of the server's + * trusted roots. This is necessary to support fingerprint authentication. + */ + /* store a pointer to ourselfs (needed by callback) */ + gnutls_session_set_ptr(pThis->sess, (void*)pThis); + iRet = gtlsLoadOurCertKey(pThis); /* first load .pem files */ + if(iRet == RS_RET_OK) { + gnutls_certificate_client_set_retrieve_function(xcred, gtlsClientCertCallback); + } else if(iRet != RS_RET_CERTLESS) { + FINALIZE; /* we have an error case! */ + } + + /* 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)); + gtlsSetTransportPtr(pThis, sock); + + /* we need to store the hostname as an alternate mean of authentication if no + * permitted peer names are given. Using the hostname is quite useful. It permits + * auto-configuration of security if a commen root cert is present. -- rgerhards, 2008-05-26 + */ + CHKmalloc(pThis->pszConnectHost = (uchar*)strdup((char*)host)); + + /* and perform the handshake */ + CHKgnutls(gnutls_handshake(pThis->sess)); + dbgprintf("GnuTLS handshake succeeded\n"); + + /* now check if the remote peer is permitted to talk to us - ideally, we + * should do this during the handshake, but GnuTLS does not yet provide + * the necessary callbacks -- rgerhards, 2008-05-26 + */ + CHKiRet(gtlsChkPeerAuth(pThis)); + +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->SetMode = SetMode; + pIf->SetAuthMode = SetAuthMode; + pIf->SetPermPeers =SetPermPeers; + pIf->CheckConnection = CheckConnection; + 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(net, LM_NET_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(net, LM_NET_FILENAME)); + 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 + nsdsel_gtlsClassExit(); + nsd_gtlsClassExit(); + pthread_mutex_destroy(&mutGtlsStrerror); +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 */ + CHKiRet(nsdsel_gtlsClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ + + pthread_mutex_init(&mutGtlsStrerror, NULL); +ENDmodInit +/* vi:set ai: + */ diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h new file mode 100644 index 00000000..52eea8ee --- /dev/null +++ b/runtime/nsd_gtls.h @@ -0,0 +1,92 @@ +/* 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" + +#define NSD_GTLS_MAX_RCVBUF 8 * 1024 /* max size of buffer for message reception */ + +typedef enum { + gtlsRtry_None = 0, /**< no call needs to be retried */ + gtlsRtry_handshake = 1, + gtlsRtry_recv = 2 +} gtlsRtryCall_t; /**< IDs of calls that needs to be retried */ + +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 */ + uchar *pszConnectHost; /**< hostname used for connect - may be used to authenticate peer if no other name given */ + int iMode; /* 0 - plain tcp, 1 - TLS */ + int bAbortConn; /* if set, abort conncection (fatal error had happened) */ + enum { + GTLS_AUTH_CERTNAME = 0, + GTLS_AUTH_CERTFINGERPRINT = 1, + GTLS_AUTH_CERTVALID = 2, + GTLS_AUTH_CERTANON = 3 + } authMode; + gtlsRtryCall_t rtryCall;/**< what must we retry? */ + int bIsInitiator; /**< 0 if socket is the server end (listener), 1 if it is the initiator */ + gnutls_session sess; + int bHaveSess; /* as we don't know exactly which gnutls_session values are invalid, we use this one + to flag whether or not we are in a session (same as -1 for a socket meaning no sess) */ + int bReportAuthErr; /* only the first auth error is to be reported, this var triggers it. Initially, it is + * set to 1 and changed to 0 after the first report. It is changed back to 1 after + * one successful authentication. */ + permittedPeers_t *pPermPeers; /* permitted peers */ + gnutls_x509_crt ourCert; /**< our certificate, if in client mode (unused in server mode) */ + gnutls_x509_privkey ourKey; /**< our private key, if in client mode (unused in server mode) */ + short bOurCertIsInit; /**< 1 if our certificate is initialized and must be deinit on destruction */ + short bOurKeyIsInit; /**< 1 if our private key is initialized and must be deinit on destruction */ + char *pszRcvBuf; + int lenRcvBuf; /**< -1: empty, 0: connection closed, 1..NSD_GTLS_MAX_RCVBUF-1: data of that size present */ + int ptrRcvBuf; /**< offset for next recv operation if 0 < lenRcvBuf < NSD_GTLS_MAX_RCVBUF */ +}; + +/* interface is defined in nsd.h, we just implement it! */ +#define nsd_gtlsCURR_IF_VERSION nsdCURR_IF_VERSION + +/* prototypes */ +PROTOTYPEObj(nsd_gtls); +/* some prototypes for things used by our nsdsel_gtls helper class */ +uchar *gtlsStrerror(int error); +rsRetVal gtlsChkPeerAuth(nsd_gtls_t *pThis); +rsRetVal gtlsRecordRecv(nsd_gtls_t *pThis); +static inline rsRetVal gtlsHasRcvInBuffer(nsd_gtls_t *pThis) { + /* we have a valid receive buffer one such is allocated and + * NOT exhausted! + */ + dbgprintf("hasRcvInBuffer on nsd %p: pszRcvBuf %p, lenRcvBuf %d\n", pThis, + pThis->pszRcvBuf, pThis->lenRcvBuf); + return(pThis->pszRcvBuf != NULL && pThis->lenRcvBuf != -1); + } + + +/* 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..c3899f83 --- /dev/null +++ b/runtime/nsd_ptcp.c @@ -0,0 +1,781 @@ +/* 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 "nsdsel_ptcp.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; +} + + +/* Set the driver mode. We support no different modes, but allow mode + * 0 to be set to be compatible with config file defaults and the other + * drivers. + * rgerhards, 2008-04-28 + */ +static rsRetVal +SetMode(nsd_t __attribute__((unused)) *pNsd, int mode) +{ + DEFiRet; + if(mode != 0) { + errmsg.LogError(0, RS_RET_INVALID_DRVR_MODE, "error: driver mode %d not supported by " + "ptcp netstream driver", mode); + ABORT_FINALIZE(RS_RET_INVALID_DRVR_MODE); + } +finalize_it: + RETiRet; +} + + +/* Set the authentication mode. For us, the following is supported: + * anon - no certificate checks whatsoever (discouraged, but supported) + * mode == NULL is valid and defaults to anon + * Actually, we do not even record the mode right now, because we can + * always work in anon mode, only. So there is no point in recording + * something if that's the only choice. What the function does is + * return an error if something is requested that we can not support. + * rgerhards, 2008-05-17 + */ +static rsRetVal +SetAuthMode(nsd_t __attribute__((unused)) *pNsd, uchar *mode) +{ + DEFiRet; + if(mode != NULL && strcasecmp((char*)mode, "anon")) { + errmsg.LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: authentication mode '%s' not supported by " + "ptcp netstream driver", mode); + ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED); + } + +finalize_it: + RETiRet; +} + + +/* Set the permitted peers. This is a dummy, always returning an + * error because we do not support fingerprint authentication. + * rgerhards, 2008-05-17 + */ +static rsRetVal +SetPermPeers(nsd_t __attribute__((unused)) *pNsd, permittedPeers_t __attribute__((unused)) *pPermPeers) +{ + DEFiRet; + + if(pPermPeers != NULL) { + errmsg.LogError(0, RS_RET_VALUE_NOT_IN_THIS_MODE, "authentication not supported by ptcp netstream driver"); + ABORT_FINALIZE(RS_RET_VALUE_NOT_IN_THIS_MODE); + } + +finalize_it: + 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(errno, 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 + */ + CHKiRet(pNS->Drvr.Construct(&pNewNsd)); + CHKiRet(pNS->Drvr.SetSock(pNewNsd, sock)); + CHKiRet(pNS->Drvr.SetMode(pNewNsd, netstrms.GetDrvrMode(pNS))); + CHKiRet(pNS->Drvr.SetAuthMode(pNewNsd, netstrms.GetDrvrAuthMode(pNS))); + CHKiRet(pNS->Drvr.SetPermPeers(pNewNsd, netstrms.GetDrvrPermPeers(pNS))); + CHKiRet(netstrms.CreateStrm(pNS, &pNewStrm)); + pNewStrm->pDrvrData = (nsd_t*) pNewNsd; + 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); + + if(*pLenBuf == 0) { + ABORT_FINALIZE(RS_RET_CLOSED); + } else if (*pLenBuf < 0) { + ABORT_FINALIZE(RS_RET_ERR); + } + +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) +{ + 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; +} + + +/* This function checks if the connection is still alive - well, kind of... It + * is primarily being used for plain TCP syslog and it is quite a hack. However, + * as it seems to work, it is worth supporting it. The bottom line is that it + * should not be called by anything else but a plain tcp syslog sender. + * In order for it to work, it must be called *immediately* *before* the send() + * call. For details about what is done, see here: + * http://blog.gerhards.net/2008/06/getting-bit-more-reliability-from-plain.html + * rgerhards, 2008-06-09 + */ +static void +CheckConnection(nsd_t *pNsd) +{ + int rc; + char msgbuf[1]; /* dummy */ + nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd; + ISOBJ_TYPE_assert(pThis, nsd_ptcp); + + rc = recv(pThis->sock, msgbuf, 1, MSG_DONTWAIT | MSG_PEEK); + if(rc == 0) { + dbgprintf("CheckConnection detected broken connection - closing it\n"); + /* in this case, the remote peer had shut down the connection and we + * need to close our side, too. + */ + sockClose(&pThis->sock); + } +} + + +/* 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->SetMode = SetMode; + pIf->SetAuthMode = SetAuthMode; + pIf->SetPermPeers = SetPermPeers; + pIf->Rcv = Rcv; + pIf->Send = Send; + pIf->LstnInit = LstnInit; + pIf->AcceptConnReq = AcceptConnReq; + pIf->Connect = Connect; + pIf->GetRemoteHName = GetRemoteHName; + pIf->GetRemoteIP = GetRemoteIP; + pIf->CheckConnection = CheckConnection; +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, DONT_LOAD_LIB); + 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(netstrms, LM_NETSTRMS_FILENAME)); + CHKiRet(objUse(netstrm, DONT_LOAD_LIB)); + + /* set our own handlers */ +ENDObjClassInit(nsd_ptcp) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + nsdsel_ptcpClassExit(); + 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 */ + CHKiRet(nsdsel_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..c3a93bee --- /dev/null +++ b/runtime/nsdsel_gtls.c @@ -0,0 +1,260 @@ +/* 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 "nsd_ptcp.h" +#include "nsdsel_ptcp.h" +#include "nsdsel_gtls.h" + +/* 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); + if(pNsdGTLS->iMode == 1) { + if(waitOp == NSDSEL_RD && gtlsHasRcvInBuffer(pNsdGTLS)) { + ++pThis->iBufferRcvReady; + FINALIZE; + } + if(pNsdGTLS->rtryCall != gtlsRtry_None) { + if(gnutls_record_get_direction(pNsdGTLS->sess) == 0) { + CHKiRet(nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, NSDSEL_RD)); + } else { + CHKiRet(nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, NSDSEL_WR)); + } + FINALIZE; + } + } + + /* if we reach this point, we need no special handling */ + CHKiRet(nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, waitOp)); + +finalize_it: + 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); + if(pThis->iBufferRcvReady > 0) { + /* we still have data ready! */ + *piNumReady = pThis->iBufferRcvReady; + } else { + iRet = nsdsel_ptcp.Select(pThis->pTcp, piNumReady); + } + + RETiRet; +} + + +/* retry an interrupted GTLS operation + * rgerhards, 2008-04-30 + */ +static rsRetVal +doRetry(nsd_gtls_t *pNsd) +{ + DEFiRet; + int gnuRet; + + dbgprintf("GnuTLS requested retry of %d operation - executing\n", pNsd->rtryCall); + + /* We follow a common scheme here: first, we do the systen call and + * then we check the result. So far, the result is checked after the + * switch, because the result check is the same for all calls. Note that + * this may change once we deal with the read and write calls (but + * probably this becomes an issue only when we begin to work on TLS + * for relp). -- rgerhards, 2008-04-30 + */ + switch(pNsd->rtryCall) { + case gtlsRtry_handshake: + gnuRet = gnutls_handshake(pNsd->sess); + if(gnuRet == 0) { + pNsd->rtryCall = gtlsRtry_None; /* we are done */ + /* we got a handshake, now check authorization */ + CHKiRet(gtlsChkPeerAuth(pNsd)); + } + break; + case gtlsRtry_recv: + dbgprintf("retrying gtls recv, nsd: %p\n", pNsd); + CHKiRet(gtlsRecordRecv(pNsd)); + pNsd->rtryCall = gtlsRtry_None; /* we are done */ + gnuRet = 0; + break; + default: + assert(0); /* this shall not happen! */ + dbgprintf("ERROR: pNsd->rtryCall invalid in nsdsel_gtls.c:%d\n", __LINE__); + gnuRet = 0; /* if it happens, we have at least a defined behaviour... ;) */ + break; + } + + if(gnuRet == 0) { + pNsd->rtryCall = gtlsRtry_None; /* we are done */ + } else if(gnuRet != GNUTLS_E_AGAIN && gnuRet != GNUTLS_E_INTERRUPTED) { + uchar *pErr = gtlsStrerror(gnuRet); + dbgprintf("unexpected GnuTLS error %d in %s:%d: %s\n", gnuRet, __FILE__, __LINE__, pErr); + free(pErr); + pNsd->rtryCall = gtlsRtry_None; /* we are also done... ;) */ + ABORT_FINALIZE(RS_RET_GNUTLS_ERR); + } + /* if we are interrupted once again (else case), we do not need to + * change our status because we are already setup for retries. + */ + +finalize_it: + if(iRet != RS_RET_OK && iRet != RS_RET_CLOSED && iRet != RS_RET_RETRY) + pNsd->bAbortConn = 1; /* request abort */ + 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); + if(pNsdGTLS->iMode == 1) { + if(waitOp == NSDSEL_RD && gtlsHasRcvInBuffer(pNsdGTLS)) { + *pbIsReady = 1; + FINALIZE; + } + if(pNsdGTLS->rtryCall != gtlsRtry_None) { + CHKiRet(doRetry(pNsdGTLS)); + /* we used this up for our own internal processing, so the socket + * is not ready from the upper layer point of view. + */ + *pbIsReady = 0; + FINALIZE; + } + } + + CHKiRet(nsdsel_ptcp.IsReady(pThis->pTcp, pNsdGTLS->pTcp, waitOp, pbIsReady)); + +finalize_it: + 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_CORE_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_NSD_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_CORE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(nsdsel_ptcp, LM_NSD_PTCP_FILENAME)); + + /* set our own handlers */ +ENDObjClassInit(nsdsel_gtls) +/* vi:set ai: + */ diff --git a/runtime/nsdsel_gtls.h b/runtime/nsdsel_gtls.h new file mode 100644 index 00000000..709ccd03 --- /dev/null +++ b/runtime/nsdsel_gtls.h @@ -0,0 +1,43 @@ +/* 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) */ + int iBufferRcvReady; /* number of descriptiors where no RD select is needed because we have data in buf */ +}; + +/* interface is defined in nsd.h, we just implement it! */ +#define nsdsel_gtlsCURR_IF_VERSION nsdCURR_IF_VERSION + +/* prototypes */ +PROTOTYPEObj(nsdsel_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..41b85e0c --- /dev/null +++ b/runtime/nsdsel_ptcp.c @@ -0,0 +1,196 @@ +/* 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" + +/* 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_CORE_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_CORE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + /* set our own handlers */ +ENDObjClassInit(nsdsel_ptcp) +/* vi:set ai: + */ diff --git a/runtime/nsdsel_ptcp.h b/runtime/nsdsel_ptcp.h new file mode 100644 index 00000000..6c0c7fa7 --- /dev/null +++ b/runtime/nsdsel_ptcp.h @@ -0,0 +1,44 @@ +/* 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); + +#endif /* #ifndef INCLUDED_NSDSEL_PTCP_H */ diff --git a/runtime/nssel.c b/runtime/nssel.c new file mode 100644 index 00000000..d11d5fe1 --- /dev/null +++ b/runtime/nssel.c @@ -0,0 +1,227 @@ +/* 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" + +/* 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. Please note that + * we do some name-mangeling. We know that each nsd driver also needs + * a nssel driver. So we simply append "sel" to the nsd driver name: This, + * of course, means that the driver name must match these rules, but that + * shouldn't be a real problem. + * WARNING: this code is mostly identical to similar code in + * netstrms.c - TODO: abstract it and move it to some common place. + * rgerhards, 2008-04-28 + */ +static rsRetVal +loadDrvr(nssel_t *pThis) +{ + DEFiRet; + uchar *pBaseDrvrName; + uchar szDrvrName[48]; /* 48 shall be large enough */ + + pBaseDrvrName = pThis->pBaseDrvrName; + if(pBaseDrvrName == NULL) /* if no drvr name is set, use system default */ + pBaseDrvrName = glbl.GetDfltNetstrmDrvr(); + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmnsdsel_%s", pBaseDrvrName) == sizeof(szDrvrName)) + ABORT_FINALIZE(RS_RET_DRVRNAME_TOO_LONG); + CHKmalloc(pThis->pDrvrName = (uchar*) strdup((char*)szDrvrName)); + + 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__, szDrvrName+2, DONT_LOAD_LIB, (void*) &pThis->Drvr)); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pThis->pDrvrName != NULL) + free(pThis->pDrvrName); + pThis->pDrvrName = NULL; + } + 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); + + /* and now we must release our driver, if we got one. We use the presence of + * a driver name string as load indicator (because we also need that string + * to release the driver + */ + if(pThis->pDrvrName != NULL) { + obj.ReleaseObj(__FILE__, pThis->pDrvrName+2, DONT_LOAD_LIB, (void*) &pThis->Drvr); + free(pThis->pDrvrName); + } +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) +/* vi:set ai: + */ diff --git a/runtime/nssel.h b/runtime/nssel.h new file mode 100644 index 00000000..8cb34f5a --- /dev/null +++ b/runtime/nssel.h @@ -0,0 +1,56 @@ +/* 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 "netstrms.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 *pBaseDrvrName; /**< nsd base driver name to use, or NULL if system default */ + uchar *pDrvrName; /**< full base driver name (set when driver is loaded) */ + 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 LM_NETSTRMS_FILENAME + +#endif /* #ifndef INCLUDED_NSSEL_H */ diff --git a/obj-types.h b/runtime/obj-types.h index 4cd45153..914c2f2c 100644 --- a/obj-types.h +++ b/runtime/obj-types.h @@ -7,22 +7,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 OBJ_TYPES_H_INCLUDED @@ -63,13 +64,13 @@ typedef enum { /* IDs of base methods supported by all objects - used for jump t /* the base data type for interfaces * This MUST be in sync with the ifBEGIN macro */ -typedef struct interface_s { +struct interface_s { int ifVersion; /* must be set to version requested */ int ifIsLoaded; /* is the interface loaded? (0-no, 1-yes, 2-load failed; if not 1, functions can NOT be called! */ -} interface_t; +}; -typedef struct objInfo_s { +struct objInfo_s { uchar *pszID; /* the object ID as a string */ size_t lenID; /* length of the ID string */ int iObjVers; @@ -77,16 +78,16 @@ typedef struct objInfo_s { rsRetVal (*objMethods[OBJ_NUM_METHODS])(); rsRetVal (*QueryIF)(interface_t*); struct modInfo_s *pModInfo; -} objInfo_t; +}; -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 @@ -105,14 +106,28 @@ 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) # define ISOBJ_assert(pObj) #endif +/* a set method for *very simple* object accesses. Note that this does + * NOT conform to the standard calling conventions and should be + * used only if actually nothing can go wrong! -- rgerhards, 2008-04-17 + */ +#define DEFpropGetMeth(obj, prop, dataType)\ + dataType obj##Get##prop(void)\ + { \ + return pThis->prop = pVal; \ + } + #define DEFpropSetMethPTR(obj, prop, dataType)\ rsRetVal obj##Set##prop(obj##_t *pThis, dataType *pVal)\ { \ @@ -312,8 +327,8 @@ rsRetVal objName##ClassExit(void) \ } /* ------------------------------ object loader system ------------------------------ * - * The following code is the early beginning of a dynamic object loader system. The - * root idea is that all objects will become dynamically loadable libraries over time, + * The following code builds a dynamic object loader system. The + * root idea is that all objects are dynamically loadable, * which is necessary to get a clean plug-in interface where every plugin can access * rsyslog's rich object model via simple and quite portable methods. * @@ -326,17 +341,12 @@ rsRetVal objName##ClassExit(void) \ * macros create a static variable named like the object in each calling objects * static data block. * - * To facilitate moving to this system, I begin to implement some hooks, which - * allows to use interfaces today (when the rest of the infrastructure is not yet - * there). This is in the hope that it will ease migration to the full-fledged system - * once we are ready to work on that. - * rgerhards, 2008-02-21 + * rgerhards, 2008-02-21 (initial implementation), 2008-04-17 (update of this note) */ /* this defines the QueryInterface print entry point. Over time, it should be * present in all objects. */ -//#define PROTOTYPEObjQueryInterface(obj) rsRetVal obj##QueryInterface(obj##_if_t *pThis) #define BEGINobjQueryInterface(obj) \ rsRetVal obj##QueryInterface(obj##_if_t *pIf) \ { \ @@ -355,7 +365,7 @@ rsRetVal objName##ClassExit(void) \ */ #define BEGINinterface(obj) \ typedef struct obj##_if_s {\ - ifBEGIN; /* This MUST always be the first interface member */ + ifBEGIN /* This MUST always be the first interface member */ #define ENDinterface(obj) \ } obj##_if_t; @@ -371,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) \ @@ -396,7 +403,7 @@ rsRetVal objName##ClassExit(void) \ */ #define PROTOTYPEObj(obj) \ PROTOTYPEObjClassInit(obj); \ - PROTOTYPEObjClassExit(obj); + PROTOTYPEObjClassExit(obj) /* ------------------------------ end object loader system ------------------------------ */ @@ -50,22 +50,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -422,7 +423,7 @@ finalize_it: /* define a helper to make code below a bit cleaner (and quicker to write) */ -#define NEXTC CHKiRet(strmReadChar(pStrm, &c))//;dbgprintf("c: %c\n", c); +#define NEXTC CHKiRet(strmReadChar(pStrm, &c))/*;dbgprintf("c: %c\n", c)*/ /* de-serialize an embedded, non-octect-counted string. This is useful @@ -802,7 +803,7 @@ Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixu } } while(iRetLocal != RS_RET_OK); - if(rsCStrSzStrCmp(pstrID, pszTypeExpected, strlen((char*)pszTypeExpected))) // TODO: optimize strlen() - caller shall provide + if(rsCStrSzStrCmp(pstrID, pszTypeExpected, strlen((char*)pszTypeExpected))) /* TODO: optimize strlen() - caller shall provide */ ABORT_FINALIZE(RS_RET_INVALID_OID); CHKiRet(FindObjInfo(pstrID, &pObjInfo)); @@ -826,7 +827,7 @@ Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixu finalize_it: if(iRet != RS_RET_OK && pObj != NULL) - free(pObj); // TODO: check if we can call destructor 2008-01-13 rger + free(pObj); /* TODO: check if we can call destructor 2008-01-13 rger */ if(pstrID != NULL) rsCStrDestruct(&pstrID); @@ -1011,13 +1012,6 @@ FindObjInfo(cstr_t *pstrOID, objInfo_t **ppInfo) bFound = 0; i = 0; while(!bFound && i < OBJ_NUM_IDS) { -#if 0 -RUNLOG_VAR("%d", i); -if(arrObjInfo[i] != NULL) { -RUNLOG_VAR("%p", arrObjInfo[i]->pszID); -RUNLOG_VAR("%s", arrObjInfo[i]->pszID); -} -#endif if(arrObjInfo[i] != NULL && !rsCStrSzStrCmp(pstrOID, arrObjInfo[i]->pszID, arrObjInfo[i]->lenID)) { bFound = 1; break; @@ -1078,7 +1072,7 @@ RegisterObj(uchar *pszObjName, objInfo_t *pInfo) finalize_it: if(iRet != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "registering object '%s' failed with error code %d", pszObjName, iRet); + errmsg.LogError(0, NO_ERRCODE, "registering object '%s' failed with error code %d", pszObjName, iRet); } RETiRet; @@ -1198,15 +1192,14 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) objInfo_t *pObjInfo; - dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); + /* dev debug only dbgprintf("source file %s releasing object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */ if(pObjFile == NULL) 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? */ - } - if(pIf->ifIsLoaded == 2) { + ABORT_FINALIZE(RS_RET_OK); /* we are not loaded - this is perfectly OK... */ + } else if(pIf->ifIsLoaded == 2) { pIf->ifIsLoaded = 0; /* clean up */ ABORT_FINALIZE(RS_RET_OK); /* we had a load error and can not continue */ } @@ -1215,7 +1208,6 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) CHKiRet(FindObjInfo(pStr, &pObjInfo)); /* if we reach this point, we have a valid pObjInfo */ - //if(pObjInfo->pModInfo != NULL) { /* NULL means core module */ module.Release(srcFile, &pObjInfo->pModInfo); /* decrease refcount */ pIf->ifIsLoaded = 0; /* indicated "no longer valid" */ @@ -1292,10 +1284,10 @@ objClassExit(void) /* TODO: implement the class exits! */ #if 0 - errmsgClassInit(pModInfo); cfsyslineInit(pModInfo); varClassInit(pModInfo); #endif + errmsgClassExit(); moduleClassExit(); RETiRet; } @@ -23,22 +23,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 OBJ_H_INCLUDED diff --git a/objomsr.c b/runtime/objomsr.c index 6a617ad1..21d284f3 100644 --- a/objomsr.c +++ b/runtime/objomsr.c @@ -5,22 +5,23 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" @@ -140,6 +141,5 @@ int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int return RS_RET_OK; } -/* - * vi:set ai: +/* vim:set ai: */ diff --git a/objomsr.h b/runtime/objomsr.h index 9fdddf69..2255e4f3 100644 --- a/objomsr.h +++ b/runtime/objomsr.h @@ -2,22 +2,23 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 OBJOMSR_H_INCLUDED diff --git a/queue.c b/runtime/queue.c index 081f48b3..00f811a0 100644 --- a/queue.c +++ b/runtime/queue.c @@ -43,7 +43,6 @@ #include <errno.h> #include "rsyslog.h" -#include "syslogd.h" #include "queue.h" #include "stringbuf.h" #include "srUtils.h" @@ -53,6 +52,7 @@ /* static data */ DEFobjStaticHelpers +DEFobjCurrIf(glbl) /* forward-definitions */ rsRetVal queueChkPersist(queue_t *pThis); @@ -642,7 +642,7 @@ queueLoadPersStrmInfoFixup(strm_t *pStrm, queue_t __attribute__((unused)) *pThis DEFiRet; ISOBJ_TYPE_assert(pStrm, strm); ISOBJ_TYPE_assert(pThis, queue); - CHKiRet(strmSetDir(pStrm, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(pStrm, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); finalize_it: RETiRet; } @@ -667,7 +667,7 @@ queueHaveQIF(queue_t *pThis) /* Construct file name */ lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi", - (char*) glblGetWorkDir(), (char*)pThis->pszFilePrefix); + (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix); /* check if the file exists */ if(stat((char*) pszQIFNam, &stat_buf) == -1) { @@ -704,7 +704,7 @@ queueTryLoadPersistedInfo(queue_t *pThis) /* Construct file name */ lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi", - (char*) glblGetWorkDir(), (char*)pThis->pszFilePrefix); + (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix); /* check if the file exists */ if(stat((char*) pszQIFNam, &stat_buf) == -1) { @@ -791,7 +791,7 @@ static rsRetVal qConstructDisk(queue_t *pThis) ; } else { CHKiRet(strmConstruct(&pThis->tVars.disk.pWrite)); - CHKiRet(strmSetDir(pThis->tVars.disk.pWrite, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(pThis->tVars.disk.pWrite, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pWrite, 10000000)); CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE)); CHKiRet(strmSetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR)); @@ -799,7 +799,7 @@ static rsRetVal qConstructDisk(queue_t *pThis) CHKiRet(strmConstruct(&pThis->tVars.disk.pRead)); CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 1)); - CHKiRet(strmSetDir(pThis->tVars.disk.pRead, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(pThis->tVars.disk.pRead, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pRead, 10000000)); CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pRead, STREAMMODE_READ)); CHKiRet(strmSetsType(pThis->tVars.disk.pRead, STREAMTYPE_FILE_CIRCULAR)); @@ -815,8 +815,8 @@ static rsRetVal qConstructDisk(queue_t *pThis) * for example file name generation must not be changed as that would break the * ability to read existing queue files. -- rgerhards, 2008-01-12 */ -CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pWrite, pThis->iMaxFileSize)); -CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pRead, pThis->iMaxFileSize)); + CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pWrite, pThis->iMaxFileSize)); + CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pRead, pThis->iMaxFileSize)); finalize_it: RETiRet; @@ -849,8 +849,12 @@ static rsRetVal qAddDisk(queue_t *pThis, void* pUsr) pThis->tVars.disk.sizeOnDisk += nWriteCount; - /* The following line is a backport from 3.19.10 - fixes mem leak */ + /* we have enqueued the user element to disk. So we now need to destruct + * the in-memory representation. The instance will be re-created upon + * dequeue. -- rgerhards, 2008-07-09 + */ objDestruct(pUsr); + dbgoprint((obj_t*) pThis, "write wrote %lld octets to disk, queue disk size now %lld octets\n", nWriteCount, pThis->tVars.disk.sizeOnDisk); @@ -1261,7 +1265,7 @@ rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads, /* we have an object, so let's fill the properties */ objConstructSetObjInfo(pThis); - if((pThis->pszSpoolDir = (uchar*) strdup((char*)glblGetWorkDir())) == NULL) + if((pThis->pszSpoolDir = (uchar*) strdup((char*)glbl.GetWorkDir())) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); /* set some water marks so that we have useful defaults if none are set specifically */ @@ -1874,7 +1878,7 @@ static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint) /* Construct file name */ lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi", - (char*) glblGetWorkDir(), (char*)pThis->pszFilePrefix); + (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix); if((bIsCheckpoint != QUEUE_CHECKPOINT) && (queueGetOverallQueueSize(pThis) == 0)) { if(pThis->bNeedDelQIF) { @@ -2082,10 +2086,6 @@ finalize_it: /* enqueue a new user data element * Enqueues the new element and awakes worker thread. - * TODO: this code still uses the "discard if queue full" approach from - * the main queue. This needs to be reconsidered or, better, done via a - * caller-selectable parameter mode. For the time being, I leave it in. - * rgerhards, 2008-01-03 */ rsRetVal queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr) @@ -2166,21 +2166,6 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr) } } -#if 0 // previous code, remove when done with advanced flow control - /* wait for the queue to be ready... */ - while( (pThis->iMaxQueueSize > 0 && pThis->iQueueSize >= pThis->iMaxQueueSize) - || (pThis->qType == QUEUETYPE_DISK && pThis->sizeOnDiskMax != 0 - && pThis->tVars.disk.sizeOnDisk > pThis->sizeOnDiskMax)) { - dbgoprint((obj_t*) pThis, "enqueueMsg: queue FULL - waiting to drain.\n"); - timeoutComp(&t, pThis->toEnq); - if(pthread_cond_timedwait(&pThis->notFull, pThis->mut, &t) != 0) { - dbgoprint((obj_t*) pThis, "enqueueMsg: cond timeout, dropping message!\n"); - objDestruct(pUsr); - ABORT_FINALIZE(RS_RET_QUEUE_FULL); - } - } -#endif - /* and finally enqueue the message */ CHKiRet(queueAdd(pThis, pUsr)); queueChkPersist(pThis); @@ -2255,24 +2240,24 @@ finalize_it: /* some simple object access methods */ -DEFpropSetMeth(queue, iPersistUpdCnt, int); -DEFpropSetMeth(queue, iDeqtWinFromHr, int); -DEFpropSetMeth(queue, iDeqtWinToHr, int); -DEFpropSetMeth(queue, toQShutdown, long); -DEFpropSetMeth(queue, toActShutdown, long); -DEFpropSetMeth(queue, toWrkShutdown, long); -DEFpropSetMeth(queue, toEnq, long); -DEFpropSetMeth(queue, iHighWtrMrk, int); -DEFpropSetMeth(queue, iLowWtrMrk, int); -DEFpropSetMeth(queue, iDiscardMrk, int); -DEFpropSetMeth(queue, iFullDlyMrk, int); -DEFpropSetMeth(queue, iDiscardSeverity, int); -DEFpropSetMeth(queue, bIsDA, int); -DEFpropSetMeth(queue, iMinMsgsPerWrkr, int); -DEFpropSetMeth(queue, bSaveOnShutdown, int); -DEFpropSetMeth(queue, pUsr, void*); -DEFpropSetMeth(queue, iDeqSlowdown, int); -DEFpropSetMeth(queue, sizeOnDiskMax, int64); +DEFpropSetMeth(queue, iPersistUpdCnt, int) +DEFpropSetMeth(queue, iDeqtWinFromHr, int) +DEFpropSetMeth(queue, iDeqtWinToHr, int) +DEFpropSetMeth(queue, toQShutdown, long) +DEFpropSetMeth(queue, toActShutdown, long) +DEFpropSetMeth(queue, toWrkShutdown, long) +DEFpropSetMeth(queue, toEnq, long) +DEFpropSetMeth(queue, iHighWtrMrk, int) +DEFpropSetMeth(queue, iLowWtrMrk, int) +DEFpropSetMeth(queue, iDiscardMrk, int) +DEFpropSetMeth(queue, iFullDlyMrk, int) +DEFpropSetMeth(queue, iDiscardSeverity, int) +DEFpropSetMeth(queue, bIsDA, int) +DEFpropSetMeth(queue, iMinMsgsPerWrkr, int) +DEFpropSetMeth(queue, bSaveOnShutdown, int) +DEFpropSetMeth(queue, pUsr, void*) +DEFpropSetMeth(queue, iDeqSlowdown, int) +DEFpropSetMeth(queue, sizeOnDiskMax, int64) /* This function can be used as a generic way to set properties. Only the subset @@ -2315,6 +2300,7 @@ rsRetVal queueQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } */ BEGINObjClassInit(queue, 1, OBJ_IS_CORE_MODULE) /* request objects we use */ + CHKiRet(objUse(glbl, CORE_COMPONENT)); /* now set our own handlers */ OBJSetMethodHandler(objMethod_SETPROPERTY, queueSetProperty); diff --git a/queue.h b/runtime/queue.h index 9e75b31b..9e75b31b 100644 --- a/queue.h +++ b/runtime/queue.h diff --git a/regexp.c b/runtime/regexp.c index 86b3e6c4..86b3e6c4 100644 --- a/regexp.c +++ b/runtime/regexp.c diff --git a/regexp.h b/runtime/regexp.h index 8f6ac891..8f6ac891 100644 --- a/regexp.h +++ b/runtime/regexp.h diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c new file mode 100644 index 00000000..54db12c2 --- /dev/null +++ b/runtime/rsyslog.c @@ -0,0 +1,237 @@ +/* rsyslog.c - the main entry point into rsyslog's runtime library (RTL) + * + * This module contains all function which work on a RTL global level. It's + * name is abbreviated to "rsrt" (rsyslog runtime). + * + * Please note that the runtime library tends to be plugin-safe. That is, it must be + * initialized by calling a global initialization function. However, that + * function checks if the library is already initialized and, if so, does + * nothing except incrementing a refeence count. Similarly, the deinit + * function does nothing as long as there are still other users (which + * is tracked via the refcount). As such, it is safe to call init and + * exit multiple times, as long as this are always matching calls. This + * capability is needed for a plugin system, where one plugin never + * knows what the other did. HOWEVER, as of this writing, not all runtime + * library objects may work cleanly without static global data (the + * debug system is a very good example of this). So while we aim at the + * ability to work well in a plugin environment, things may not really work + * out. If you intend to use the rsyslog runtime library inside plugins, + * you should investigate the situation in detail. Please note that the + * rsyslog project itself does not yet need this functionality - thus you + * can safely assume it is totally untested ;). + * + * rgerhards, 2008-04-17: I have now once again checked on the plugin-safety. + * Unfortunately, there is currently no hook at all with which we could + * abstract a global data instance class. As such, we can NOT make the + * runtime plugin-safe in the above-described sense. As the rsyslog + * project itself does not need this functionality (and it is quesationable + * if someone else ever will), we do currently do not make an effort to + * support it. So if you intend to use rsyslog runtime inside a non-rsyslog + * plugin system, be careful! + * + * The rsyslog runtime library is in general reentrant and thread-safe. There + * are some intentional exceptions (e.g. inside the msg object). These are + * documented. Any other threading and reentrency issue can be considered a bug. + * + * Module begun 2008-04-16 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "rsyslog.h" +#include "obj.h" +#include "vm.h" +#include "sysvar.h" +#include "stringbuf.h" +#include "wti.h" +#include "wtp.h" +#include "expr.h" +#include "ctok.h" +#include "vmop.h" +#include "vmstk.h" +#include "vmprg.h" +#include "datetime.h" +#include "queue.h" +#include "conf.h" +#include "glbl.h" +#include "errmsg.h" + +/* forward definitions */ +static rsRetVal dfltErrLogger(int, uchar *errMsg); + +/* globally visible static data - see comment in rsyslog.h for details */ +uchar *glblModPath; /* module load path */ +rsRetVal (*glblErrLogger)(int, uchar*) = dfltErrLogger; /* the error logger to use by the errmsg module */ + +/* static data */ +static int iRefCount = 0; /* our refcount - it MUST exist only once inside a process (not thread) + thus it is perfectly OK to use a static. MUST be initialized to 0! */ + +/* This is the default instance of the error logger. It simply writes the message + * to stderr. It is expected that this is replaced by the runtime user very early + * during startup (at least if the default is unsuitable). However, we provide a + * default so that we can log errors during the intial phase, most importantly + * during initialization. -- rgerhards. 2008-04-17 + */ +static rsRetVal dfltErrLogger(int iErr, uchar *errMsg) +{ + DEFiRet; + fprintf(stderr, "rsyslog runtime error(%d): %s\n", iErr, errMsg); + RETiRet; +} + + +/* set the error log function + * rgerhards, 2008-04-18 + */ +rsRetVal +rsrtSetErrLogger(rsRetVal (*errLogger)(int, uchar*)) +{ + DEFiRet; + assert(errLogger != NULL); + glblErrLogger = errLogger; + RETiRet; +} + + +/* globally initialze the runtime system + * NOTE: this is NOT thread safe and must not be called concurrently. If that + * ever poses a problem, we may use proper mutex calls - not considered needed yet. + * If ppErrObj is provided, it receives a char pointer to the name of the object that + * caused the problem (if one occured). The caller must never free this pointer. If + * ppErrObj is NULL, no such information will be provided. pObjIF is the pointer to + * the "obj" object interface, which may be used to query any other rsyslog objects. + * rgerhards, 2008-04-16 + */ +rsRetVal +rsrtInit(char **ppErrObj, obj_if_t *pObjIF) +{ + DEFiRet; + + if(iRefCount == 0) { + /* init runtime only if not yet done */ + if(ppErrObj != NULL) *ppErrObj = "obj"; + CHKiRet(objClassInit(NULL)); /* *THIS* *MUST* always be the first class initilizer being called! */ + CHKiRet(objGetObjInterface(pObjIF)); /* this provides the root pointer for all other queries */ + + /* initialize core classes. We must be very careful with the order of events. Some + * classes use others and if we do not initialize them in the right order, we may end + * up with an invalid call. The most important thing that can happen is that an error + * is detected and needs to be logged, wich in turn requires a broader number of classes + * to be available. The solution is that we take care in the order of calls AND use a + * class immediately after it is initialized. And, of course, we load those classes + * first that we use ourselfs... -- rgerhards, 2008-03-07 + */ + if(ppErrObj != NULL) *ppErrObj = "glbl"; + CHKiRet(glblClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "datetime"; + CHKiRet(datetimeClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "msg"; + CHKiRet(msgClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "str,"; + CHKiRet(strmClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "wti"; + CHKiRet(wtiClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "wtp"; + CHKiRet(wtpClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "queue"; + CHKiRet(queueClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "vmstk"; + CHKiRet(vmstkClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "sysvar"; + CHKiRet(sysvarClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "vm"; + CHKiRet(vmClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "vmop"; + CHKiRet(vmopClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "vmprg"; + CHKiRet(vmprgClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "ctok_token"; + CHKiRet(ctok_tokenClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "ctok"; + CHKiRet(ctokClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "expr"; + CHKiRet(exprClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "conf"; + CHKiRet(confClassInit(NULL)); + + /* dummy "classes" */ + if(ppErrObj != NULL) *ppErrObj = "str"; + CHKiRet(strInit()); + } + + ++iRefCount; + dbgprintf("rsyslog runtime initialized, version %s, current users %d\n", VERSION, iRefCount); + +finalize_it: + RETiRet; +} + + +/* globally de-initialze the runtime system + * NOTE: this is NOT thread safe and must not be called concurrently. If that + * ever poses a problem, we may use proper mutex calls - not considered needed yet. + * This function must be provided with the caller's obj object pointer. This is + * automatically deinitialized by the runtime system. + * rgerhards, 2008-04-16 + */ +rsRetVal +rsrtExit(void) +{ + DEFiRet; + + if(iRefCount == 1) { + /* do actual de-init only if we are the last runtime user */ + confClassExit(); + glblClassExit(); + objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ + } + + --iRefCount; + /* TODO we must deinit this pointer! pObjIF = NULL; / * no longer exists for this caller */ + + dbgprintf("rsyslog runtime de-initialized, current users %d\n", iRefCount); + + RETiRet; +} + + +/* returns 0 if the rsyslog runtime is not initialized and another value + * if it is. This function is primarily meant to be used by runtime functions + * itself. However, it is safe to call it before initializing the runtime. + * Plugins should NOT rely on this function. The reason is that another caller + * may have already initialized it but deinits it before this plugin is done. + * So for plugins and like architectures, the right course of action is to + * call rsrtInit() and rsrtExit(), which can be called by multiple callers. + * rgerhards, 2008-04-16 + */ +int rsrtIsInit(void) +{ + return iRefCount; +} + + +/* vim:set ai: + */ diff --git a/rsyslog.h b/runtime/rsyslog.h index 88429631..7927af3a 100644 --- a/rsyslog.h +++ b/runtime/rsyslog.h @@ -1,27 +1,28 @@ -/* Header file with global definitions for the whole - * rsyslog project (including all subprojects like - * rfc3195d). +/* This is the header file for the rsyslog runtime. It must be included + * if someone intends to use the runtime. + * * Begun 2005-09-15 RGerhards * - * Copyright (C) 2005 by Rainer Gerhards and Adiscon GmbH + * Copyright (C) 2005-2008 by Rainer Gerhards and Adiscon GmbH * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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_RSYSLOG_H #define INCLUDED_RSYSLOG_H @@ -43,6 +44,46 @@ # define _FILE_OFFSET_BITS 64 #endif +/* portability: not all platforms have these defines, so we + * define them here if they are missing. -- rgerhards, 2008-03-04 + */ +#ifndef LOG_MAKEPRI +# define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) +#endif +#ifndef LOG_PRI +# define LOG_PRI(p) ((p) & LOG_PRIMASK) +#endif +#ifndef LOG_FAC +# define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) +#endif + + +/* define some base data types */ +typedef unsigned char uchar;/* get rid of the unhandy "unsigned char" */ +typedef struct thrdInfo thrdInfo_t; +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 permittedPeers_s permittedPeers_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */ +typedef struct permittedPeerWildcard_s permittedPeerWildcard_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */ +typedef struct tcpsrv_s tcpsrv_t; /* some universal 64 bit define... */ typedef long long int64; @@ -70,10 +111,16 @@ typedef enum { */ enum rsRetVal_ /** return value. All methods return this if not specified otherwise */ { + /* the first two define are for errmsg.logError(), so that we can use the rsRetVal + * as an rsyslog error code. -- rgerhards, 20080-06-27 + */ + RS_RET_NO_ERRCODE = -1, /**< RESERVED for NO_ERRCODE errmsg.logError status name */ + RS_RET_INCLUDE_ERRNO = 1073741824, /* 2**30 - do NOT use error codes above this! */ + /* begin regular error codes */ RS_RET_NOT_IMPLEMENTED = -7, /**< implementation is missing (probably internal error or lazyness ;)) */ RS_RET_OUT_OF_MEMORY = -6, /**< memory allocation failed */ RS_RET_PROVIDED_BUFFER_TOO_SMALL = -50,/**< the caller provided a buffer, but the called function sees the size of this buffer is too small - operation not carried out */ - RS_RET_TRUE = -1, /**< to indicate a true state (can be used as TRUE, legacy) */ + RS_RET_TRUE = -3, /**< to indicate a true state (can be used as TRUE, legacy) */ RS_RET_FALSE = -2, /**< to indicate a false state (can be used as FALSE, legacy) */ RS_RET_NO_IRET = -8, /**< This is a trick for the debuging system - it means no iRet is provided */ RS_RET_ERR = -3000, /**< generic failure */ @@ -171,7 +218,36 @@ 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_QUEUE_FULL = -2074, /**< queue is full, operation could not be completed */ + RS_RET_MALICIOUS_HNAME = -2074, /**< remote peer is trying malicious things with its hostname */ + 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 */ + RS_RET_INVALID_DRVR_MODE = -2081, /**< tried to set mode not supported by driver */ + RS_RET_DRVRNAME_TOO_LONG = -2082, /**< driver name too long - should never happen */ + RS_RET_TLS_HANDSHAKE_ERR = -2083, /**< TLS handshake failed */ + RS_RET_TLS_CERT_ERR = -2084, /**< generic TLS certificate error */ + RS_RET_TLS_NO_CERT = -2085, /**< no TLS certificate available where one was expected */ + RS_RET_VALUE_NOT_SUPPORTED = -2086, /**< a provided value is not supported */ + RS_RET_VALUE_NOT_IN_THIS_MODE = -2087, /**< a provided value is invalid for the curret mode */ + RS_RET_INVALID_FINGERPRINT = -2088, /**< a fingerprint is not valid for this use case */ + RS_RET_CONNECTION_ABORTREQ = -2089, /**< connection was abort requested due to previous error */ + RS_RET_CERT_INVALID = -2090, /**< a x509 certificate failed validation */ + RS_RET_CERT_INVALID_DN = -2091, /**< distinguised name in x509 certificate is invalid (e.g. wrong escaping) */ + RS_RET_CERT_EXPIRED = -2092, /**< we are past a x.509 cert's expiration time */ + RS_RET_CERT_NOT_YET_ACTIVE = -2094, /**< x.509 cert's activation time not yet reached */ + RS_RET_SYS_ERR = -2095, /**< system error occured (e.g. time() returned -1, quite unexpected) */ + RS_RET_FILE_NO_STAT = -2096, /**< can not stat() a file */ + RS_RET_FILE_TOO_LARGE = -2097, /**< a file is larger than permitted */ + RS_RET_INVALID_WILDCARD = -2098, /**< a wildcard entry is invalid */ + RS_RET_CLOSED = -2099, /**< connection was closed */ + RS_RET_RETRY = -2100, /**< call should be retried (e.g. EGAIN on recv) */ + RS_RET_GSS_ERR = -2101, /**< generic error occured in GSSAPI subsystem */ + RS_RET_CERTLESS = -2102, /**< state: we run without machine cert (this may be OK) */ + RS_RET_QUEUE_FULL = -2103, /**< queue is full, operation could not be completed */ + RS_RET_ACCEPT_ERR = -2104, /**< error during accept() system call */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ @@ -182,7 +258,6 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_NO_RUN = 3, /**< operation successful, but function does not like to be executed */ RS_RET_OK = 0 /**< operation successful */ }; -typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */ /* some helpful macros to work with srRetVals. * Be sure to call the to-be-returned variable always "iRet" and @@ -247,9 +322,6 @@ typedef enum rsObjectID rsObjID; #define RSFREEOBJ(x) {(x)->OID = OIDrsFreed; free(x);} #endif -/* get rid of the unhandy "unsigned char" - */ -typedef unsigned char uchar; /* for the time being, we do our own portability handling here. It * looks like autotools either does not yet support checks for it, or @@ -263,8 +335,26 @@ typedef unsigned char uchar; void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2))); #include "debug.h" +#include "obj.h" + +/* the variable below is a trick: before we can init the runtime, the caller + * may want to set a module load path. We can not do this via the glbl class + * because it needs an initialized runtime system (and may at some point in time + * even be loaded itself). So this is a no-go. What we do is use a single global + * variable which may be provided with a pointer by the caller. This variable + * resides in rsyslog.c, the main runtime file. We have not seen any realy valule + * in providing object access functions. If you don't like that, feel free to + * add them. -- rgerhards, 2008-04-17 + */ +extern uchar *glblModPath; /* module load path */ +extern rsRetVal (*glblErrLogger)(int, uchar*); + +/* some runtime prototypes */ +rsRetVal rsrtInit(char **ppErrObj, obj_if_t *pObjIF); +rsRetVal rsrtExit(void); +int rsrtIsInit(void); +rsRetVal rsrtSetErrLogger(rsRetVal (*errLogger)(int, uchar*)); #endif /* multi-include protection */ -/* - * vi:set ai: +/* vim:set ai: */ diff --git a/srUtils.h b/runtime/srUtils.h index ebd6518f..bfce4cbb 100644 --- a/srUtils.h +++ b/runtime/srUtils.h @@ -7,22 +7,23 @@ * * Copyright 2003-2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 __SRUTILS_H_INCLUDED__ #define __SRUTILS_H_INCLUDED__ 1 @@ -90,6 +91,7 @@ void mutexCancelCleanup(void *arg); void srSleep(int iSeconds, int iuSeconds); char *rs_strerror_r(int errnum, char *buf, size_t buflen); int decodeSyslogName(uchar *name, syslogName_t *codetab); +int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep); /* mutex operations */ /* some macros to cancel-safe lock a mutex (it will automatically be released diff --git a/srUtils.c b/runtime/srutils.c index fa451b7e..97cc3252 100644 --- a/srUtils.c +++ b/runtime/srutils.c @@ -9,26 +9,28 @@ * * Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 <string.h> #include <unistd.h> @@ -39,16 +41,16 @@ #include <assert.h> #include <sys/wait.h> #include <ctype.h> -#include "liblogging-stub.h" #define TRUE 1 #define FALSE 0 #include "srUtils.h" -#include "syslogd.h" #include "obj.h" /* here we host some syslog specific names. There currently is no better place * to do it, but over here is also not ideal... -- rgerhards, 2008-02-14 + * rgerhards, 2008-04-16: note in LGPL move: the code tables below exist in + * the same way in BSD, so it is not a problem to move them from GPLv3 to LGPL. */ syslogName_t syslogPriNames[] = { {"alert", LOG_ALERT}, @@ -502,5 +504,51 @@ int decodeSyslogName(uchar *name, syslogName_t *codetab) } +/** + * getSubString + * + * Copy a string byte by byte until the occurrence + * of a given separator. + * + * \param ppSrc Pointer to a pointer of the source array of characters. If a + separator detected the Pointer points to the next char after the + separator. Except if the end of the string is dedected ('\n'). + Then it points to the terminator char. + * \param pDst Pointer to the destination array of characters. Here the substing + will be stored. + * \param DstSize Maximum numbers of characters to store. + * \param cSep Separator char. + * \ret int Returns 0 if no error occured. + * + * rgerhards, 2008-02-12: some notes are due... I will once again fix this function, this time + * so that it treats ' ' as a request for whitespace. But in general, the function and its callers + * should be changed over time, this is not really very good code... + */ +int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep) +{ + uchar *pSrc = *ppSrc; + int iErr = 0; /* 0 = no error, >0 = error */ + while((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0' && DstSize>1) { + *pDst++ = *(pSrc)++; + DstSize--; + } + /* check if the Dst buffer was to small */ + if ((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0') { + dbgprintf("in getSubString, error Src buffer > Dst buffer\n"); + iErr = 1; + } + if (*pSrc == '\0' || *pSrc == '\n') + /* this line was missing, causing ppSrc to be invalid when it + * was returned in case of end-of-string. rgerhards 2005-07-29 + */ + *ppSrc = pSrc; + else + *ppSrc = pSrc+1; + *pDst = '\0'; + return iErr; +} + + + /* vim:set ai: */ diff --git a/stream.c b/runtime/stream.c index 1be4571a..f1f69cc8 100644 --- a/stream.c +++ b/runtime/stream.c @@ -41,7 +41,6 @@ #include <errno.h> #include "rsyslog.h" -#include "syslogd.h" #include "stringbuf.h" #include "srUtils.h" #include "obj.h" @@ -644,7 +643,7 @@ DEFpropSetMeth(strm, iMaxFileSize, int) DEFpropSetMeth(strm, iFileNumDigits, int) DEFpropSetMeth(strm, tOperationsMode, int) DEFpropSetMeth(strm, tOpenMode, mode_t) -DEFpropSetMeth(strm, sType, strmType_t); +DEFpropSetMeth(strm, sType, strmType_t) rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal) { @@ -910,7 +909,7 @@ CODESTARTobjQueryInterface(strm) * work here (if we can support an older interface version - that, * of course, also affects the "if" above). */ - //xxxpIf->oID = OBJvm; + /*xxxpIf->oID = OBJvm; SAMPLE */ finalize_it: ENDobjQueryInterface(strm) diff --git a/stream.h b/runtime/stream.h index 371358ab..371358ab 100644 --- a/stream.h +++ b/runtime/stream.h diff --git a/stringbuf.c b/runtime/stringbuf.c index 4254d5bd..93d1e1ef 100644 --- a/stringbuf.c +++ b/runtime/stringbuf.c @@ -7,24 +7,25 @@ * All functions in this "class" start with rsCStr (rsyslog Counted String). * begun 2005-09-07 rgerhards * - * Copyright (C) 2007 by Rainer Gerhards and Adiscon GmbH + * Copyright (C) 2007-2008 by Rainer Gerhards and Adiscon GmbH * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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" diff --git a/stringbuf.h b/runtime/stringbuf.h index e44e86e1..c1966449 100644 --- a/stringbuf.h +++ b/runtime/stringbuf.h @@ -15,22 +15,23 @@ * Copyright 2005 * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 _STRINGBUF_H_INCLUDED__ #define _STRINGBUF_H_INCLUDED__ 1 diff --git a/syslogd-types.h b/runtime/syslogd-types.h index 9aea3778..be0dfdd8 100644 --- a/syslogd-types.h +++ b/runtime/syslogd-types.h @@ -6,28 +6,28 @@ * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 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. * - * Rsyslog is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * GNU Lesser 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/>. + * 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 SYSLOGD_TYPES_INCLUDED #define SYSLOGD_TYPES_INCLUDED 1 #include "stringbuf.h" -//#include "net.h" #include <sys/param.h> #if HAVE_SYSLOG_H #include <syslog.h> @@ -99,6 +99,5 @@ struct syslogTime { typedef struct syslogTime syslogTime_t; #endif /* #ifndef SYSLOGD_TYPES_INCLUDED */ -/* - * vi:set ai: +/* vi:set ai: */ diff --git a/sysvar.c b/runtime/sysvar.c index 5eec8f67..5eec8f67 100644 --- a/sysvar.c +++ b/runtime/sysvar.c diff --git a/sysvar.h b/runtime/sysvar.h index 35051b64..35051b64 100644 --- a/sysvar.h +++ b/runtime/sysvar.h @@ -198,7 +198,6 @@ CODESTARTop(CMP_CONTAINS) bRes = (rsCStrLocateInSzStr(operand2->val.pStr, rsCStrGetSzStr(operand1->val.pStr)) == -1) ? 0 : 1; /* we have a result, so let's push it */ -RUNLOG_VAR("%lld", bRes); \ PUSHRESULTop(operand1, bRes); var.Destruct(&operand2); /* no longer needed */ ENDop(CMP_CONTAINS) @@ -218,7 +217,6 @@ var.DebugPrint(operand2); \ bRes = (rsCStrCaseInsensitiveLocateInSzStr(operand2->val.pStr, rsCStrGetSzStr(operand1->val.pStr)) == -1) ? 0 : 1; /* we have a result, so let's push it */ -RUNLOG_VAR("%lld", bRes); \ PUSHRESULTop(operand1, bRes); var.Destruct(&operand2); /* no longer needed */ ENDop(CMP_CONTAINSI) @@ -237,7 +235,6 @@ CODESTARTop(CMP_STARTSWITH) rsCStrLen(operand2->val.pStr)) == 0) ? 1 : 0; /* we have a result, so let's push it */ -RUNLOG_VAR("%lld", bRes); \ PUSHRESULTop(operand1, bRes); var.Destruct(&operand2); /* no longer needed */ ENDop(CMP_STARTSWITH) diff --git a/vmprg.c b/runtime/vmprg.c index a2b744d7..a2b744d7 100644 --- a/vmprg.c +++ b/runtime/vmprg.c diff --git a/vmprg.h b/runtime/vmprg.h index db1f62f0..db1f62f0 100644 --- a/vmprg.h +++ b/runtime/vmprg.h diff --git a/vmstk.c b/runtime/vmstk.c index 1ee3d485..1ee3d485 100644 --- a/vmstk.c +++ b/runtime/vmstk.c diff --git a/vmstk.h b/runtime/vmstk.h index 2d45ee4d..2d45ee4d 100644 --- a/vmstk.h +++ b/runtime/vmstk.h @@ -40,7 +40,6 @@ #include <errno.h> #include "rsyslog.h" -#include "syslogd.h" #include "stringbuf.h" #include "srUtils.h" #include "wtp.h" @@ -341,6 +340,7 @@ wtiWorkerCancelCleanup(void *arg) * and would be very hard to debug. The yield() is a sure fix, its performance overhead * should be well accepted given the above facts. -- rgerhards, 2008-01-10 */ +#pragma GCC diagnostic ignored "-Wempty-body" rsRetVal wtiWorker(wti_t *pThis) { @@ -427,10 +427,11 @@ wtiWorker(wti_t *pThis) RETiRet; } +#pragma GCC diagnostic warning "-Wempty-body" /* some simple object access methods */ -DEFpropSetMeth(wti, pWtp, wtp_t*); +DEFpropSetMeth(wti, pWtp, wtp_t*) /* set the debug header message * The passed-in string is duplicated. So if the caller does not need @@ -41,7 +41,6 @@ #include <errno.h> #include "rsyslog.h" -#include "syslogd.h" #include "stringbuf.h" #include "srUtils.h" #include "wtp.h" @@ -245,6 +244,7 @@ wtpChkStopWrkr(wtp_t *pThis, int bLockMutex, int bLockUsrMutex) } +#pragma GCC diagnostic ignored "-Wempty-body" /* Send a shutdown command to all workers and see if they terminate. * A timeout may be specified. * rgerhards, 2008-01-14 @@ -293,6 +293,7 @@ wtpShutdownAll(wtp_t *pThis, wtpState_t tShutdownCmd, struct timespec *ptTimeout RETiRet; } +#pragma GCC diagnostic warning "-Wempty-body" /* indicate that a thread has terminated and awake anyone waiting on it @@ -383,6 +384,7 @@ wtpWrkrExecCancelCleanup(void *arg) * wti worker. * rgerhards, 2008-01-21 */ +#pragma GCC diagnostic ignored "-Wempty-body" static void * wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in wtp! */ { @@ -436,6 +438,7 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in ENDfunc pthread_exit(0); } +#pragma GCC diagnostic warning "-Wempty-body" /* start a new worker */ @@ -539,20 +542,20 @@ finalize_it: /* some simple object access methods */ -DEFpropSetMeth(wtp, toWrkShutdown, long); -DEFpropSetMeth(wtp, wtpState, wtpState_t); -DEFpropSetMeth(wtp, iNumWorkerThreads, int); -DEFpropSetMeth(wtp, pUsr, void*); -DEFpropSetMethPTR(wtp, pmutUsr, pthread_mutex_t); -DEFpropSetMethPTR(wtp, pcondBusy, pthread_cond_t); -DEFpropSetMethFP(wtp, pfChkStopWrkr, rsRetVal(*pVal)(void*, int)); -DEFpropSetMethFP(wtp, pfRateLimiter, rsRetVal(*pVal)(void*)); -DEFpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, int)); -DEFpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*, int)); -DEFpropSetMethFP(wtp, pfOnIdle, rsRetVal(*pVal)(void*, int)); -DEFpropSetMethFP(wtp, pfOnWorkerCancel, rsRetVal(*pVal)(void*, void*)); -DEFpropSetMethFP(wtp, pfOnWorkerStartup, rsRetVal(*pVal)(void*)); -DEFpropSetMethFP(wtp, pfOnWorkerShutdown, rsRetVal(*pVal)(void*)); +DEFpropSetMeth(wtp, toWrkShutdown, long) +DEFpropSetMeth(wtp, wtpState, wtpState_t) +DEFpropSetMeth(wtp, iNumWorkerThreads, int) +DEFpropSetMeth(wtp, pUsr, void*) +DEFpropSetMethPTR(wtp, pmutUsr, pthread_mutex_t) +DEFpropSetMethPTR(wtp, pcondBusy, pthread_cond_t) +DEFpropSetMethFP(wtp, pfChkStopWrkr, rsRetVal(*pVal)(void*, int)) +DEFpropSetMethFP(wtp, pfRateLimiter, rsRetVal(*pVal)(void*)) +DEFpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, int)) +DEFpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*, int)) +DEFpropSetMethFP(wtp, pfOnIdle, rsRetVal(*pVal)(void*, int)) +DEFpropSetMethFP(wtp, pfOnWorkerCancel, rsRetVal(*pVal)(void*, void*)) +DEFpropSetMethFP(wtp, pfOnWorkerStartup, rsRetVal(*pVal)(void*)) +DEFpropSetMethFP(wtp, pfOnWorkerShutdown, rsRetVal(*pVal)(void*)) /* return the current number of worker threads. @@ -38,10 +38,9 @@ #if HAVE_FCNTL_H #include <fcntl.h> #endif -#include "syslogd.h" +#include "dirty.h" #include "syslogd-types.h" #include "net.h" -#include "tcpsyslog.h" #include "tcpclt.h" #include "module-template.h" #include "srUtils.h" @@ -52,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) @@ -307,16 +305,23 @@ Send(tcpclt_t *pThis, void *pData, char *msg, size_t len) /* we are done, we also use this as indication that the previous * message was succesfully received (it's not always the case, but its at * least our best shot at it -- rgerhards, 2008-03-12 + * As of 2008-06-09, we have implemented an algorithm which detects connection + * loss quite good in some (common) scenarios. Thus, the probability of + * message duplication due to the code below has increased. We so now have + * a config setting, default off, that enables the user to request retransmits. + * However, if not requested, we do NOT need to do all the stuff needed for it. */ - if(pThis->prevMsg != NULL) - free(pThis->prevMsg); - /* if we can not alloc a new buffer, we silently ignore it. The worst that - * happens is that we lose our message recovery buffer - anything else would - * be worse, so don't try anything ;) -- rgerhards, 2008-03-12 - */ - if((pThis->prevMsg = malloc(len)) != NULL) { - memcpy(pThis->prevMsg, msg, len); - pThis->lenPrevMsg = len; + if(pThis->bResendLastOnRecon == 1) { + if(pThis->prevMsg != NULL) + free(pThis->prevMsg); + /* if we can not alloc a new buffer, we silently ignore it. The worst that + * happens is that we lose our message recovery buffer - anything else would + * be worse, so don't try anything ;) -- rgerhards, 2008-03-12 + */ + if((pThis->prevMsg = malloc(len)) != NULL) { + memcpy(pThis->prevMsg, msg, len); + pThis->lenPrevMsg = len; + } } /* we are done with this record */ @@ -326,7 +331,8 @@ Send(tcpclt_t *pThis, void *pData, char *msg, size_t len) ++retry; CHKiRet(pThis->prepRetryFunc(pData)); /* try to recover */ /* now try to send our stored previous message (which most probably - * didn't make it + * didn't make it. Note that if bResendLastOnRecon is 0, prevMsg will + * never become non-NULL, so the check below covers all cases. */ if(pThis->prevMsg != NULL) { CHKiRet(pThis->initFunc(pData)); @@ -348,6 +354,13 @@ finalize_it: /* set functions */ static rsRetVal +SetResendLastOnRecon(tcpclt_t *pThis, int bResendLastOnRecon) +{ + DEFiRet; + pThis->bResendLastOnRecon = (short) bResendLastOnRecon; + RETiRet; +} +static rsRetVal SetSendInit(tcpclt_t *pThis, rsRetVal (*pCB)(void*)) { DEFiRet; @@ -427,6 +440,7 @@ CODESTARTobjQueryInterface(tcpclt) pIf->Send = Send; /* set functions */ + pIf->SetResendLastOnRecon = SetResendLastOnRecon; pIf->SetSendInit = SetSendInit; pIf->SetSendFrame = SetSendFrame; pIf->SetSendPrepRetry = SetSendPrepRetry; @@ -26,7 +26,6 @@ #ifndef TCPCLT_H_INCLUDED #define TCPCLT_H_INCLUDED 1 -#include "tcpsyslog.h" #include "obj.h" /* the tcpclt object */ @@ -34,6 +33,7 @@ typedef struct tcpclt_s { BEGINobjInstance; /**< Data to implement generic object - MUST be the first data element! */ TCPFRAMINGMODE tcp_framing; char *prevMsg; + short bResendLastOnRecon; /* should the last message be resent on a successful reconnect? */ size_t lenPrevMsg; /* session specific callbacks */ rsRetVal (*initFunc)(void*); @@ -50,12 +50,13 @@ BEGINinterface(tcpclt) /* name must also be changed in ENDinterface macro! */ int (*Send)(tcpclt_t *pThis, void*pData, char*msg, size_t len); int (*CreateSocket)(struct addrinfo *addrDest); /* set methods */ + rsRetVal (*SetResendLastOnRecon)(tcpclt_t*, int); rsRetVal (*SetSendInit)(tcpclt_t*, rsRetVal (*)(void*)); rsRetVal (*SetSendFrame)(tcpclt_t*, rsRetVal (*)(void*, char*, size_t)); rsRetVal (*SetSendPrepRetry)(tcpclt_t*, rsRetVal (*)(void*)); rsRetVal (*SetFraming)(tcpclt_t*, TCPFRAMINGMODE framing); ENDinterface(tcpclt) -#define tcpcltCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define tcpcltCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ /* prototypes */ diff --git a/tcps_sess.c b/tcps_sess.c index 001f32f0..b93bb115 100644 --- a/tcps_sess.c +++ b/tcps_sess.c @@ -27,35 +27,27 @@ * * 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 "syslogd.h" +#include "dirty.h" #include "module-template.h" #include "net.h" #include "tcpsrv.h" #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,8 @@ CODESTARTobjDestruct(tcps_sess) /* now destruct our own properties */ if(pThis->fromHost != NULL) free(pThis->fromHost); - close(pThis->sock); + if(pThis->fromHostIP != NULL) + free(pThis->fromHostIP); ENDobjDestruct(tcps_sess) @@ -110,6 +102,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. -- rgerhards, 2008-04-24 + */ static rsRetVal SetHost(tcps_sess_t *pThis, uchar *pszHost) { @@ -119,25 +115,43 @@ 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; + + RETiRet; +} + +/* set the remote host's IP. Note that the caller *hands over* the string. That is, + * the caller no longer controls it once SetHostIP() has received it. Most importantly, + * the caller must not free it. -- rgerhards, 2008-05-16 + */ +static rsRetVal +SetHostIP(tcps_sess_t *pThis, uchar *pszHostIP) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pThis, tcps_sess); + + if(pThis->fromHostIP != NULL) { + free(pThis->fromHostIP); + } + + pThis->fromHostIP = pszHostIP; -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) { @@ -148,7 +162,7 @@ SetMsgIdx(tcps_sess_t *pThis, int idx) } -/* set out parent, the tcpsrv object */ +/* set our parent, the tcpsrv object */ static rsRetVal SetTcpsrv(tcps_sess_t *pThis, tcpsrv_t *pSrv) { @@ -200,16 +214,15 @@ 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(0, 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 * this case. */ dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n"); - parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); + parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); pThis->bAtStrtOfFram = 1; } @@ -228,10 +241,11 @@ 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... */ + free(pThis->fromHostIP); + pThis->fromHostIP = NULL; /* not really needed, but... */ RETiRet; } @@ -266,13 +280,13 @@ processDataRcvd(tcps_sess_t *pThis, char c) } 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(NO_ERRCODE, "Framing Error in received TCP message: " + 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(NO_ERRCODE, "Framing Error in received TCP message: " + errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: " "invalid octet count %d.\n", pThis->iOctetsRemain); } else if(pThis->iOctetsRemain > MAXLINE) { /* while we can not do anything against it, we can at least log an indication @@ -280,7 +294,7 @@ processDataRcvd(tcps_sess_t *pThis, char c) */ dbgprintf("truncating message with %d octets - MAXLINE is %d\n", pThis->iOctetsRemain, MAXLINE); - errmsg.LogError(NO_ERRCODE, "received oversize message: size is %d bytes, " + errmsg.LogError(0, NO_ERRCODE, "received oversize message: size is %d bytes, " "MAXLINE is %d, truncating...\n", pThis->iOctetsRemain, MAXLINE); } pThis->inputState = eInMsg; @@ -290,7 +304,7 @@ processDataRcvd(tcps_sess_t *pThis, char c) if(pThis->iMsg >= MAXLINE) { /* emergency, we now need to flush, no matter if we are at end of message or not... */ dbgprintf("error: message received is larger than MAXLINE, we split it\n"); - parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); + parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); pThis->iMsg = 0; /* 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 @@ -300,7 +314,7 @@ processDataRcvd(tcps_sess_t *pThis, char c) } if(c == '\n' && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delemiter? */ - parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); + parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); pThis->iMsg = 0; pThis->inputState = eAtStrtFram; } else { @@ -318,7 +332,7 @@ processDataRcvd(tcps_sess_t *pThis, char c) pThis->iOctetsRemain--; if(pThis->iOctetsRemain < 1) { /* we have end of frame! */ - parseAndSubmitMessage(pThis->fromHost, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); + parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->msg, pThis->iMsg, MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY); pThis->iMsg = 0; pThis->inputState = eAtStrtFram; } @@ -345,27 +359,13 @@ static rsRetVal DataRcvd(tcps_sess_t *pThis, char *pData, size_t iLen) { DEFiRet; - char *pMsg; char *pEnd; ISOBJ_TYPE_assert(pThis, tcps_sess); assert(pData != NULL); assert(iLen > 0); - /* We now copy the message to the session buffer. As - * it looks, we need to do this in any case because - * we might run into multiple messages inside a single - * buffer. Of course, we could think about optimizations, - * but as this code is to be replaced by liblogging, it - * probably doesn't make so much sense... - * rgerhards 2005-07-04 - * - * Algo: - * - copy message to buffer until the first LF is found - * - printline() the buffer - * - continue with copying - */ - pMsg = pThis->msg; /* just a shortcut */ + /* We now copy the message to the session buffer. */ pEnd = pData + iLen; /* this is one off, which is intensional */ while(pData < pEnd) { @@ -403,7 +403,8 @@ CODESTARTobjQueryInterface(tcps_sess) pIf->SetUsrP = SetUsrP; pIf->SetTcpsrv = SetTcpsrv; pIf->SetHost = SetHost; - pIf->SetSock = SetSock; + pIf->SetHostIP = SetHostIP; + pIf->SetStrm = SetStrm; pIf->SetMsgIdx = SetMsgIdx; finalize_it: ENDobjQueryInterface(tcps_sess) @@ -416,6 +417,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_NETSTRMS_FILENAME); ENDObjClassExit(tcps_sess) @@ -426,6 +428,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_NETSTRMS_FILENAME)); /* set our own handlers */ OBJSetMethodHandler(objMethod_DEBUGPRINT, tcps_sessDebugPrint); diff --git a/tcps_sess.h b/tcps_sess.h index 0433fdfb..ff7c167a 100644 --- a/tcps_sess.h +++ b/tcps_sess.h @@ -28,18 +28,12 @@ /* a forward-definition, we are somewhat cyclic */ struct tcpsrv_s; -/* framing modes for TCP */ -typedef enum _TCPFRAMINGMODE { - TCP_FRAMING_OCTET_STUFFING = 0, /* traditional LF-delimited */ - TCP_FRAMING_OCTET_COUNTING = 1 /* -transport-tls like octet count */ - } TCPFRAMINGMODE; - /* the tcps_sess object */ 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, @@ -48,8 +42,9 @@ typedef struct tcps_sess_s { } inputState; /* our current state */ int iOctetsRemain; /* Number of Octets remaining in message */ TCPFRAMINGMODE eFraming; - char msg[MAXLINE+1]; - char *fromHost; + uchar msg[MAXLINE+1]; + uchar *fromHost; + uchar *fromHostIP; void *pUsr; /* a user-pointer */ } tcps_sess_t; @@ -67,7 +62,8 @@ 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 (*SetHostIP)(tcps_sess_t *pThis, uchar*); + 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! */ @@ -54,7 +54,7 @@ #include <fcntl.h> #endif #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" #include "cfsysline.h" #include "module-template.h" #include "net.h" @@ -62,43 +62,28 @@ #include "conf.h" #include "tcpsrv.h" #include "obj.h" +#include "glbl.h" +#include "netstrms.h" +#include "netstrm.h" +#include "nssel.h" #include "errmsg.h" MODULE_TYPE_LIB /* defines */ #define TCPSESS_MAX_DEFAULT 200 /* default for nbr of tcp sessions if no number is given */ +#define TCPLSTN_MAX_DEFAULT 20 /* default for nbr of listeners */ /* static data */ DEFobjStaticHelpers DEFobjCurrIf(conf) +DEFobjCurrIf(glbl) DEFobjCurrIf(tcps_sess) DEFobjCurrIf(errmsg) DEFobjCurrIf(net) - - - -/* 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 @@ -136,7 +121,7 @@ configureTCPListen(tcpsrv_t *pThis, char *cOptarg) if( i >= 0 && i <= 65535) { pThis->TCPLstnPort = cOptarg; } else { - errmsg.LogError(NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg); + errmsg.LogError(0, NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg); } } @@ -196,11 +181,15 @@ TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr) { register int i; + BEGINfunc ISOBJ_TYPE_assert(pThis, tcpsrv); - for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) + assert(pThis->pSessions != NULL); + for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) { if(pThis->pSessions[i] != NULL) break; + } + ENDfunc return((i < pThis->iSessMax) ? i : -1); } @@ -213,54 +202,70 @@ 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]); - /* now get next... */ - iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess); + if(pThis->pSessions != NULL) { + /* close all TCP connections! */ + i = TCPSessGetNxtSess(pThis, -1); + while(i != -1) { + tcps_sess.Destruct(&pThis->pSessions[i]); + /* now get next... */ + i = TCPSessGetNxtSess(pThis, i); + } + + /* we are done with the session table - so get rid of it... */ + free(pThis->pSessions); + pThis->pSessions = NULL; /* just to make sure... */ } - - /* we are done with the session table - so get rid of it... - */ - free(pThis->pSessions); - pThis->pSessions = NULL; /* just to make sure... */ if(pThis->TCPLstnPort != NULL) free(pThis->TCPLstnPort); - /* finally close the listen sockets themselfs */ - freeAllSockets(&pThis->pSocksLstn); + /* 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 @@ -268,121 +273,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 = family; - hints.ai_socktype = SOCK_STREAM; - - error = getaddrinfo(NULL, TCPLstnPort, &hints, &res); - if(error) { - errmsg.LogError(NO_ERRCODE, "%s", gai_strerror(error)); - return NULL; - } - - /* Count max number of sockets we may open */ - for (maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++) - /* EMPTY */; - socks = malloc((maxs+1) * sizeof(int)); - if (socks == NULL) { - errmsg.LogError(NO_ERRCODE, "couldn't allocate memory for TCP listen sockets, suspending TCP message reception."); - freeaddrinfo(res); - return NULL; - } - - *socks = 0; /* num of sockets counter at start of array */ - s = socks + 1; - for (r = res; r != NULL ; r = r->ai_next) { - *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); - if (*s < 0) { - if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT)) - errmsg.LogError(NO_ERRCODE, "create_tcp_socket(), socket"); - /* it is debatable if PF_INET with EAFNOSUPPORT should - * also be ignored... - */ - continue; - } - -#ifdef IPV6_V6ONLY - if (r->ai_family == AF_INET6) { - int iOn = 1; - if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, - (char *)&iOn, sizeof (iOn)) < 0) { - errmsg.LogError(NO_ERRCODE, "TCP setsockopt"); - close(*s); - *s = -1; - continue; - } - } -#endif - if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, - (char *) &on, sizeof(on)) < 0 ) { - errmsg.LogError(NO_ERRCODE, "TCP setsockopt(REUSEADDR)"); - close(*s); - *s = -1; - continue; - } + TCPLstnPort = (uchar*)pThis->TCPLstnPort; - /* We need to enable BSD compatibility. Otherwise an attacker - * could flood our log files by sending us tons of ICMP errors. - */ -#ifndef OS_BSD - if(net.should_use_so_bsdcompat()) { - if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT, - (char *) &on, sizeof(on)) < 0) { - errmsg.LogError(NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)"); - close(*s); - *s = -1; - continue; - } - } -#endif - - if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0) -#ifndef IPV6_V6ONLY - && (errno != EADDRINUSE) -#endif - ) { - errmsg.LogError(NO_ERRCODE, "TCP bind"); - close(*s); - *s = -1; - continue; - } - - if( listen(*s,pThis->iSessMax / 10 + 5) < 0) { - /* If the listen fails, it most probably fails because we ask - * for a too-large backlog. So in this case we first set back - * to a fixed, reasonable, limit that should work. Only if - * that fails, too, we give up. - */ - errmsg.LogError(NO_ERRCODE, "listen with a backlog of %d failed - retrying with default of 32.", - pThis->iSessMax / 10 + 5); - if(listen(*s, 32) < 0) { - errmsg.LogError(NO_ERRCODE, "TCP listen, suspending tcp inet"); - close(*s); - *s = -1; - continue; - } - } - - (*socks)++; - s++; - } - - if(res != NULL) - freeaddrinfo(res); - - if(Debug && *socks != maxs) - dbgprintf("We could initialize %d TCP listen sockets out of %d we received " - "- this may or may not be an error indication.\n", *socks, maxs); - - if(*socks == 0) { - errmsg.LogError(NO_ERRCODE, "No TCP listen socket could successfully be initialized, " - "message reception via TCP disabled.\n"); - free(socks); - return(NULL); - } + /* 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 @@ -392,12 +286,12 @@ static int *create_tcp_socket(tcpsrv_t *pThis) * session table, so we can not continue. We need to free all * we have assigned so far, because we can not really use it... */ - errmsg.LogError(NO_ERRCODE, "Could not initialize TCP session table, suspending TCP message reception."); - freeAllSockets(&socks); /* prevent a socket leak */ - return(NULL); + errmsg.LogError(0, RS_RET_ERR, "Could not initialize TCP session table, suspending TCP message reception."); + ABORT_FINALIZE(RS_RET_ERR); } - return(socks); +finalize_it: + RETiRet; } @@ -412,34 +306,26 @@ 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; + uchar *fromHostIP = 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; + errmsg.LogError(0, RS_RET_MAX_SESS_REACHED, "too many tcp sessions - dropping incoming request"); + ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED); } else { /* we found a free spot and can construct our session object */ CHKiRet(tcps_sess.Construct(&pSess)); @@ -448,40 +334,30 @@ 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(pNewStrm, &fromHostFQDN)); + CHKiRet(netstrm.GetRemoteIP(pNewStrm, &fromHostIP)); + /* 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 */ -RUNLOG_VAR("%p", ppSess); -RUNLOG_VAR("%p", pSess); if(!pThis->pIsPermittedHost((struct sockaddr*) &addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) { - dbgprintf("%s is not an allowed sender\n", (char *) fromHostFQDN); - if(option_DisallowWarning) { + 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(0, RS_RET_HOST_NOT_PERMITTED, "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.SetHostIP(pSess, fromHostIP)); + 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,80 +376,72 @@ 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. */ +#pragma GCC diagnostic ignored "-Wempty-body" 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; + ssize_t iRcvd; 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) { + 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]); -RUNLOG_VAR("%p", &pNewSess); - 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 */ } } @@ -581,61 +449,95 @@ RUNLOG_VAR("%p", &pNewSess); /* 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)); - if(state == 0) { + iRet = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf), &iRcvd); + switch(iRet) { + case RS_RET_CLOSED: pThis->pOnRegularClose(pThis->pSessions[iTCPSess]); tcps_sess.Destruct(&pThis->pSessions[iTCPSess]); - } else if(state == -1) { - errmsg.LogError(NO_ERRCODE, "TCP session %d will be closed, error ignored\n", fdSess); - pThis->pOnErrClose(pThis->pSessions[iTCPSess]); - tcps_sess.Destruct(&pThis->pSessions[iTCPSess]); - } else { + break; + case RS_RET_RETRY: + /* we simply ignore retry - this is not an error, but we also have not received anything */ + break; + case RS_RET_OK: /* valid data received, process it! */ - if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, state) != RS_RET_OK) { + if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, iRcvd) != RS_RET_OK) { /* in this case, something went awfully wrong. * We are instructed to terminate the session. */ - errmsg.LogError(NO_ERRCODE, "Tearing down TCP Session %d - see " + errmsg.LogError(0, NO_ERRCODE, "Tearing down TCP Session %d - see " "previous messages for reason(s)\n", iTCPSess); pThis->pOnErrClose(pThis->pSessions[iTCPSess]); tcps_sess.Destruct(&pThis->pSessions[iTCPSess]); } + break; + default: + errno = 0; + errmsg.LogError(0, iRet, "netstream session %p will be closed due to error\n", + pThis->pSessions[iTCPSess]->pStrm); + pThis->pOnErrClose(pThis->pSessions[iTCPSess]); + tcps_sess.Destruct(&pThis->pSessions[iTCPSess]); + break; } --nfds; /* indicate we have processed one */ } iTCPSess = TCPSessGetNxtSess(pThis, iTCPSess); } + CHKiRet(nssel.Destruct(&pSel)); +finalize_it: /* this is a very special case - this time only we do not exit the function, + * because that would not help us either. So we simply retry it. Let's see + * if that actually is a better idea. Exiting the loop wasn't we always + * crashed, which made sense (the rest of the engine was not prepared for + * that) -- rgerhards, 2008-05-19 + */ + /*EMPTY*/; } + /* note that this point is usually not reached */ + pthread_cleanup_pop(0); /* remove cleanup handler */ + RETiRet; } +#pragma GCC diagnostic warning "-Wempty-body" -/* Standard-Constructor - */ +/* 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)); + CHKiRet(netstrms.SetDrvrMode(pThis->pNS, pThis->iDrvrMode)); + if(pThis->pszDrvrAuthMode != NULL) + CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode)); + if(pThis->pPermPeers != NULL) + CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers)); + // TODO: set driver! + CHKiRet(netstrms.ConstructFinalize(pThis->pNS)); + + /* set up listeners */ + CHKmalloc(pThis->ppLstn = calloc(TCPLSTN_MAX_DEFAULT, sizeof(netstrm_t*))); + iRet = pThis->OpenLstnSocks(pThis); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pThis->pNS != NULL) + netstrms.Destruct(&pThis->pNS); + } RETiRet; } @@ -647,6 +549,13 @@ CODESTARTobjDestruct(tcpsrv) pThis->OnDestruct(pThis->pUsr); deinit_tcp_listener(pThis); + + if(pThis->pNS != NULL) + netstrms.Destruct(&pThis->pNS); + if(pThis->pszDrvrAuthMode != NULL) + free(pThis->pszDrvrAuthMode); + if(pThis->ppLstn != NULL) + free(pThis->ppLstn); ENDobjDestruct(tcpsrv) @@ -665,7 +574,7 @@ SetCBIsPermittedHost(tcpsrv_t *pThis, int (*pCB)(struct sockaddr *addr, char *fr } static rsRetVal -SetCBRcvData(tcpsrv_t *pThis, int (*pRcvData)(tcps_sess_t*, char*, size_t)) +SetCBRcvData(tcpsrv_t *pThis, rsRetVal (*pRcvData)(tcps_sess_t*, char*, size_t, ssize_t*)) { DEFiRet; pThis->pRcvData = pRcvData; @@ -729,7 +638,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; @@ -745,6 +654,50 @@ SetUsrP(tcpsrv_t *pThis, void *pUsr) } +/* here follows a number of methods that shuffle authentication settings down + * to the drivers. Drivers not supporting these settings may return an error + * state. + * -------------------------------------------------------------------------- */ + +/* set the driver mode -- rgerhards, 2008-04-30 */ +static rsRetVal +SetDrvrMode(tcpsrv_t *pThis, int iMode) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, tcpsrv); + pThis->iDrvrMode = iMode; + RETiRet; +} + + +/* set the driver authentication mode -- rgerhards, 2008-05-19 */ +static rsRetVal +SetDrvrAuthMode(tcpsrv_t *pThis, uchar *mode) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, tcpsrv); + CHKmalloc(pThis->pszDrvrAuthMode = (uchar*)strdup((char*)mode)); +finalize_it: + RETiRet; +} + + +/* set the driver's permitted peers -- rgerhards, 2008-05-19 */ +static rsRetVal +SetDrvrPermPeers(tcpsrv_t *pThis, permittedPeers_t *pPermPeers) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, tcpsrv); + pThis->pPermPeers = pPermPeers; + RETiRet; +} + + +/* End of methods to shuffle autentication settings to the driver.; + + * -------------------------------------------------------------------------- */ + + /* queryInterface function * rgerhards, 2008-02-29 */ @@ -770,6 +723,9 @@ CODESTARTobjQueryInterface(tcpsrv) pIf->Run = Run; pIf->SetUsrP = SetUsrP; + pIf->SetDrvrMode = SetDrvrMode; + pIf->SetDrvrAuthMode = SetDrvrAuthMode; + pIf->SetDrvrPermPeers = SetDrvrPermPeers; pIf->SetCBIsPermittedHost = SetCBIsPermittedHost; pIf->SetCBOpenLstnSocks = SetCBOpenLstnSocks; pIf->SetCBRcvData = SetCBRcvData; @@ -793,7 +749,11 @@ CODESTARTObjClassExit(tcpsrv) /* release objects we no longer need */ objRelease(tcps_sess, DONT_LOAD_LIB); objRelease(conf, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); + objRelease(netstrms, DONT_LOAD_LIB); + objRelease(nssel, DONT_LOAD_LIB); + objRelease(netstrm, LM_NETSTRMS_FILENAME); objRelease(net, LM_NET_FILENAME); ENDObjClassExit(tcpsrv) @@ -806,8 +766,12 @@ 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, DONT_LOAD_LIB)); + CHKiRet(objUse(nssel, DONT_LOAD_LIB)); CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB)); CHKiRet(objUse(conf, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); /* set our own handlers */ OBJSetMethodHandler(objMethod_DEBUGPRINT, tcpsrvDebugPrint); @@ -26,26 +26,31 @@ #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 iDrvrMode; /**< mode of the stream driver to use */ + uchar *pszDrvrAuthMode; /**< auth mode of the stream driver to use */ + permittedPeers_t *pPermPeers;/**< driver's permitted peers */ + 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 */ void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/ /* 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 (*pRcvData)(tcps_sess_t*, char*, size_t, ssize_t *); + 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,24 +60,27 @@ 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 (*SetCBRcvData)(tcpsrv_t *, int (*)(tcps_sess_t*, char*, size_t)); + rsRetVal (*SetCBOpenLstnSocks)(tcpsrv_t *, rsRetVal (*)(tcpsrv_t*)); + rsRetVal (*SetCBRcvData)(tcpsrv_t *pThis, rsRetVal (*pRcvData)(tcps_sess_t*, char*, size_t, ssize_t*)); rsRetVal (*SetCBOnListenDeinit)(tcpsrv_t*, rsRetVal (*)(void*)); rsRetVal (*SetCBOnDestruct)(tcpsrv_t*, rsRetVal (*) (void*)); rsRetVal (*SetCBOnRegularClose)(tcpsrv_t*, rsRetVal (*) (tcps_sess_t*)); rsRetVal (*SetCBOnErrClose)(tcpsrv_t*, rsRetVal (*) (tcps_sess_t*)); + rsRetVal (*SetDrvrMode)(tcpsrv_t *pThis, int iMode); + rsRetVal (*SetDrvrAuthMode)(tcpsrv_t *pThis, uchar *pszMode); + rsRetVal (*SetDrvrPermPeers)(tcpsrv_t *pThis, permittedPeers_t*); /* session specifics */ rsRetVal (*SetCBOnSessAccept)(tcpsrv_t*, rsRetVal (*) (tcpsrv_t*, tcps_sess_t*)); 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 3 /* increment whenever you change the interface structure! */ /* prototypes */ diff --git a/tcpsyslog.c b/tcpsyslog.c deleted file mode 100644 index d00731d3..00000000 --- a/tcpsyslog.c +++ /dev/null @@ -1,55 +0,0 @@ -/* tcpsyslog.c - * This is the implementation of TCP-based syslog. It includes those - * (few) things that both clients and servers need. - * - * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c) - * This file is under development and has not yet arrived at being fully - * self-contained and a real object. So far, it is mostly an excerpt - * of the "old" message code without any modifications. However, it - * helps to have things at the right place one we go to the meat of it. - * - * Copyright 2007 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 "rsyslog.h" -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <unistd.h> -#include <errno.h> -#include <assert.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 "syslogd.h" -#include "syslogd-types.h" -#include "net.h" -#include "tcpsyslog.h" -#include "srUtils.h" - - -/* vi:set ai: - */ diff --git a/tcpsyslog.h b/tcpsyslog.h deleted file mode 100644 index 13c40a92..00000000 --- a/tcpsyslog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* tcpsyslog.h - * These are the definitions for TCP-based syslog. - * - * File begun on 2007-07-21 by RGerhards (extracted from syslogd.c) - * - * Copyright 2007 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. - */ -#ifndef TCPSYSLOG_H_INCLUDED -#define TCPSYSLOG_H_INCLUDED 1 - -#include <netdb.h> - -typedef enum _TCPFRAMINGMODE { - TCP_FRAMING_OCTET_STUFFING = 0, /* traditional LF-delimited */ - TCP_FRAMING_OCTET_COUNTING = 1 /* -transport-tls like octet count */ - } TCPFRAMINGMODE; - -#endif /* #ifndef TCPSYSLOG_H_INCLUDED */ -/* - * vi:set ai: - */ @@ -33,7 +33,7 @@ #include "syslogd-types.h" #include "template.h" #include "msg.h" -#include "syslogd.h" +#include "dirty.h" #include "obj.h" #include "errmsg.h" @@ -440,6 +440,8 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe) pTpe->data.field.eDateFormat = tplFmtRFC3164Date; } else if(!strcmp((char*)Buf, "date-rfc3339")) { pTpe->data.field.eDateFormat = tplFmtRFC3339Date; + } else if(!strcmp((char*)Buf, "date-subseconds")) { + pTpe->data.field.eDateFormat = tplFmtSecFrac; } else if(!strcmp((char*)Buf, "lowercase")) { pTpe->data.field.eCaseConv = tplCaseConvLower; } else if(!strcmp((char*)Buf, "uppercase")) { @@ -516,17 +518,86 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) if(*p == ':') { ++p; /* eat ':' */ #ifdef FEATURE_REGEXP - if (*p == 'R') { + if(*p == 'R') { /* APR: R found! regex alarm ! :) */ ++p; /* eat ':' */ - if (*p != ':') { + /* first come the regex type */ + if(*p == ',') { + ++p; /* eat ',' */ + if(p[0] == 'B' && p[1] == 'R' && p[2] == 'E' && (p[3] == ',' || p[3] == ':')) { + pTpe->data.field.typeRegex = TPL_REGEX_BRE; + p += 3; /* eat indicator sequence */ + } else if(p[0] == 'E' && p[1] == 'R' && p[2] == 'E' && (p[3] == ',' || p[3] == ':')) { + pTpe->data.field.typeRegex = TPL_REGEX_ERE; + p += 3; /* eat indicator sequence */ + } else { + errmsg.LogError(0, NO_ERRCODE, "error: invalid regular expression type, rest of line %s", + (char*) p); + } + } + + /* now check for submatch ID */ + pTpe->data.field.iSubMatchToUse = 0; + if(*p == ',') { + /* in this case a number follows, which indicates which match + * shall be used. This must be a single digit. + */ + ++p; /* eat ',' */ + if(isdigit((int) *p)) { + pTpe->data.field.iSubMatchToUse = *p - '0'; + ++p; /* eat digit */ + } + } + + /* now pull what to do if we do not find a match */ + if(*p == ',') { + ++p; /* eat ',' */ + if(p[0] == 'D' && p[1] == 'F' && p[2] == 'L' && p[3] == 'T' + && (p[4] == ',' || p[4] == ':')) { + pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_DFLTSTR; + p += 4; /* eat indicator sequence */ + } else if(p[0] == 'B' && p[1] == 'L' && p[2] == 'A' && p[3] == 'N' && p[4] == 'K' + && (p[5] == ',' || p[5] == ':')) { + pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_BLANK; + p += 5; /* eat indicator sequence */ + } else if(p[0] == 'F' && p[1] == 'I' && p[2] == 'E' && p[3] == 'L' && p[4] == 'D' + && (p[5] == ',' || p[5] == ':')) { + pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_WHOLE_FIELD; + p += 5; /* eat indicator sequence */ + } else if(p[0] == ',') { /* empty, use default */ + pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_DFLTSTR; + /* do NOT eat indicator sequence, as this was already eaten - the + * comma itself is already part of the next field. + */ + } else { + errmsg.LogError(0, NO_ERRCODE, "error: invalid regular expression type, rest of line %s", + (char*) p); + } + } + + /* now check for match ID */ + pTpe->data.field.iMatchToUse = 0; + if(*p == ',') { + /* in this case a number follows, which indicates which match + * shall be used. This must be a single digit. + */ + ++p; /* eat ',' */ + if(isdigit((int) *p)) { + pTpe->data.field.iMatchToUse = *p - '0'; + ++p; /* eat digit */ + } + } + + if(*p != ':') { /* There is something more than an R , this is invalid ! */ /* Complain on extra characters */ - errmsg.LogError(NO_ERRCODE, "error: invalid character in frompos after \"R\", property: '%%%s'", + errmsg.LogError(0, NO_ERRCODE, "error: invalid character in frompos after \"R\", property: '%%%s'", (char*) *pp); } else { pTpe->data.field.has_regex = 1; + dbgprintf("we have a regexp and use match #%d, submatch #%d\n", + pTpe->data.field.iMatchToUse, pTpe->data.field.iSubMatchToUse); } } else { /* now we fall through the "regular" FromPos code */ @@ -547,7 +618,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) pTpe->data.field.has_fields = 1; if(!isdigit((int)*p)) { /* complain and use default */ - errmsg.LogError(NO_ERRCODE, "error: invalid character in frompos after \"F,\", property: '%%%s' - using 9 (HT) as field delimiter", + errmsg.LogError(0, NO_ERRCODE, "error: invalid character in frompos after \"F,\", property: '%%%s' - using 9 (HT) as field delimiter", (char*) *pp); pTpe->data.field.field_delim = 9; } else { @@ -555,7 +626,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) while(isdigit((int)*p)) iNum = iNum * 10 + *p++ - '0'; if(iNum < 0 || iNum > 255) { - errmsg.LogError(NO_ERRCODE, "error: non-USASCII delimiter character value %d in template - using 9 (HT) as substitute", iNum); + errmsg.LogError(0, NO_ERRCODE, "error: non-USASCII delimiter character value %d in template - using 9 (HT) as substitute", iNum); pTpe->data.field.field_delim = 9; } else { pTpe->data.field.field_delim = iNum; @@ -565,7 +636,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) /* invalid character after F, so we need to reject * this. */ - errmsg.LogError(NO_ERRCODE, "error: invalid character in frompos after \"F\", property: '%%%s'", + errmsg.LogError(0, NO_ERRCODE, "error: invalid character in frompos after \"F\", property: '%%%s'", (char*) *pp); } } else { @@ -622,8 +693,9 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) /* Now i compile the regex */ /* Remember that the re is an attribute of the Template entry */ if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) { -dbgprintf("compile data.field.re ptr: %p (pTpe %p)\n", (&(pTpe->data.field.re)), pTpe); - if(regexp.regcomp(&(pTpe->data.field.re), (char*) regex_char, 0) != 0) { + int iOptions; + iOptions = (pTpe->data.field.typeRegex == TPL_REGEX_ERE) ? REG_EXTENDED : 0; + if(regexp.regcomp(&(pTpe->data.field.re), (char*) regex_char, iOptions) != 0) { dbgprintf("error: can not compile regex: '%s'\n", regex_char); pTpe->data.field.has_regex = 2; } @@ -633,7 +705,7 @@ dbgprintf("compile data.field.re ptr: %p (pTpe %p)\n", (&(pTpe->data.field.re)), iRetLocal); if(bFirstRegexpErrmsg) { /* prevent flood of messages, maybe even an endless loop! */ bFirstRegexpErrmsg = 0; - errmsg.LogError(NO_ERRCODE, "regexp library could not be loaded (error %d), " + errmsg.LogError(0, NO_ERRCODE, "regexp library could not be loaded (error %d), " "regexp ignored", iRetLocal); } pTpe->data.field.has_regex = 2; @@ -47,7 +47,8 @@ struct template { enum EntryTypes { UNDEFINED = 0, CONSTANT = 1, FIELD = 2 }; enum tplFormatTypes { tplFmtDefault = 0, tplFmtMySQLDate = 1, - tplFmtRFC3164Date = 2, tplFmtRFC3339Date = 3, tplFmtPgSQLDate = 4 }; + tplFmtRFC3164Date = 2, tplFmtRFC3339Date = 3, tplFmtPgSQLDate = 4, + tplFmtSecFrac = 5}; enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseConvLower = 2 }; #include "msg.h" @@ -67,7 +68,19 @@ struct templateEntry { unsigned iToPos; /* up to that one... */ #ifdef FEATURE_REGEXP regex_t re; /* APR: this is the regular expression */ - unsigned has_regex; + short has_regex; + short iMatchToUse;/* which match should be obtained (10 max) */ + short iSubMatchToUse;/* which submatch should be obtained (10 max) */ + enum { + TPL_REGEX_BRE = 0, /* posix BRE */ + TPL_REGEX_ERE = 1 /* posix ERE */ + } typeRegex; + enum { + TPL_REGEX_NOMATCH_USE_DFLTSTR = 0, /* use the (old style) default "**NO MATCH**" string */ + TPL_REGEX_NOMATCH_USE_BLANK = 1, /* use a blank string */ + TPL_REGEX_NOMATCH_USE_WHOLE_FIELD = 2 /* use the full field contents that we were searching in*/ + } nomatchAction; /**< what to do if we do not have a match? */ + #endif unsigned has_fields; /* support for field-counting: field to extract */ unsigned char field_delim; /* support for field-counting: field delemiter char */ diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..e961c766 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,3 @@ +rscript_parse +rt_init +tmp diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..d85a56f8 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,14 @@ +check_PROGRAMS = rt_init rscript_parse +TESTS = $(check_PROGRAMS) + +test_files = testbench.h runtime-dummy.c + +rt_init_SOURCES = rt-init.c $(test_files) +rt_init_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) +rt_init_LDADD = $(rsrt_libs) $(zlib_libs) $(pthreads_libs) +rt_init_LDFLAGS = -export-dynamic + +rscript_parse_SOURCES = rscript-parse.c $(test_files) +rscript_parse_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(rsrt_cflags) +rscript_parse_LDADD = $(rsrt_libs) $(zlib_libs) $(pthreads_libs) +rscript_parse_LDFLAGS = -export-dynamic diff --git a/tests/README b/tests/README new file mode 100644 index 00000000..0ce79f63 --- /dev/null +++ b/tests/README @@ -0,0 +1,9 @@ +This directory contains the rsyslog testbench. It is slowly +evolving. New tests are always welcome. So far, most tests check +out the functionality of a single module. More complex tests are +welcome. + +For a simple sample, see rtinit.c, which does a simple +init/deinit check of the runtime system. + +rgerhards, 2008-06-13 diff --git a/tests/rscript-parse.c b/tests/rscript-parse.c new file mode 100644 index 00000000..176f3f7e --- /dev/null +++ b/tests/rscript-parse.c @@ -0,0 +1,100 @@ +/* This test checks runtime initialization and exit. Other than that, it + * also serves as the most simplistic sample of how a test can be coded. + * + * Part of the testbench for rsyslog. + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include <stdio.h> + +#include "rsyslog.h" +#include "testbench.h" +#include "ctok.h" +#include "expr.h" + +MODULE_TYPE_TESTBENCH +/* define addtional objects we need for our tests */ +DEFobjCurrIf(expr) +DEFobjCurrIf(ctok) +DEFobjCurrIf(ctok_token) + +BEGINInit +CODESTARTInit + pErrObj = "expr"; CHKiRet(objUse(expr, CORE_COMPONENT)); + pErrObj = "ctok"; CHKiRet(objUse(ctok, CORE_COMPONENT)); + pErrObj = "ctok_token"; CHKiRet(objUse(ctok_token, CORE_COMPONENT)); +ENDInit + +BEGINExit +CODESTARTExit +ENDExit + +BEGINTest + ctok_t *tok; + ctok_token_t *pToken; + expr_t *pExpr; + /* the string below is an expression as defined up to 3.19.x - note that the + * then and the space after it MUST be present! + */ + uchar szExpr[] = " $msg contains 'test' then "; + /*uchar szSynErr[] = "$msg == 1 and syntaxerror ";*/ +CODESTARTTest + /* we first need a tokenizer... */ + CHKiRet(ctok.Construct(&tok)); + CHKiRet(ctok.Setpp(tok, szExpr)); + CHKiRet(ctok.ConstructFinalize(tok)); + + /* now construct our expression */ + CHKiRet(expr.Construct(&pExpr)); + CHKiRet(expr.ConstructFinalize(pExpr)); + + /* ready to go... */ + CHKiRet(expr.Parse(pExpr, tok)); + + /* we now need to parse off the "then" - and note an error if it is + * missing... + * + * rgerhards, 2008-07-01: we disable the check below, because I can not + * find the cause of the misalignment. The problem is that pToken structure has + * a different member alignment inside the runtime library then inside of + * this program. I checked compiler options, but could not find the cause. + * Should anyone have any insight, I'd really appreciate if you drop me + * a line. + */ +#if 0 + CHKiRet(ctok.GetToken(tok, &pToken)); + if(pToken->tok != ctok_THEN) { +//printf("invalid token, probably due to invalid alignment between runtime lib and this program\n"); + ctok_token.Destruct(&pToken); + ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); + } + + ctok_token.Destruct(&pToken); /* no longer needed */ +#endif + + /* we are done, so we now need to restore things */ + CHKiRet(ctok.Destruct(&tok)); +finalize_it: + /* here we may do custom error reporting */ + if(iRet != RS_RET_OK) { + uchar *pp; + ctok.Getpp(tok, &pp); + printf("error on or before '%s'\n", pp); + } +ENDTest diff --git a/liblogging-stub.h b/tests/rt-init.c index 03315f08..aaac7ed1 100644 --- a/liblogging-stub.h +++ b/tests/rt-init.c @@ -1,26 +1,44 @@ -/* This is a (now *very slim*) stub for some liblogging
- * code we use in rsyslog.
- *
- * Copyright (C) 2004, 2007 by 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.
- */
-#ifndef __LIB3195_LIBLOGGINGSTUB_H_INCLUDED__
-#define __LIB3195_LIBLOGGINGSTUB_H_INCLUDED__ 1
-#include <stdio.h>
-#endif
+/* This test checks runtime initialization and exit. Other than that, it + * also serves as the most simplistic sample of how a test can be coded. + * + * Part of the testbench for rsyslog. + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include <stdio.h> + +#include "rsyslog.h" +#include "testbench.h" + +MODULE_TYPE_TESTBENCH + + +BEGINInit +CODESTARTInit +ENDInit + +BEGINExit +CODESTARTExit +ENDExit + +BEGINTest +CODESTARTTest +finalize_it: + /* room for custom error reporter, leave blank if not needed */ +ENDTest diff --git a/glbl.h b/tests/runtime-dummy.c index 6d08ddd5..9cddd913 100644 --- a/glbl.h +++ b/tests/runtime-dummy.c @@ -1,12 +1,10 @@ -/* Definition of globally-accessible data items. - * - * This module provides access methods to items of global scope. Most often, - * these globals serve as defaults to initialize local settings. Currently, - * many of them are either constants or global variable references. However, - * this module provides the necessary hooks to change that at any time. - * - * Please note that there currently is no glbl.c file as we do not yet - * have any implementations. +/* Testbench for rsyslog + * + * This are dummy calls for "runtime" routines which are not yet properly + * abstracted and part of the actual runtime libraries. This module tries + * to make the linker happy. Please note that it does NOT provide anything + * more but the symbols. If a test requires these functions (or functions + * that depend on them), this dummy can not be used. * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * @@ -27,14 +25,17 @@ * * A copy of the GPL can be found in the file "COPYING" in this distribution. */ +#include <stdlib.h> -#ifndef GLOBALS_H_INCLUDED -#define GLOBALS_H_INCLUDED - -#define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */ +int bReduceRepeatMsgs = 0; +int repeatinterval = 30; +int bActExecWhenPrevSusp = 0; +int iActExecOnceInterval = 1; +int MarkInterval = 30; -extern uchar *glblModPath; /* module load path */ -extern uchar *pszWorkDir; -#define glblGetWorkDir() (pszWorkDir == NULL ? (uchar*) "" : pszWorkDir) +void cflineClassic(void) {}; +void selectorAddList(void) {}; +void selectorConstruct(void) {}; +void selectorDestruct(void) {}; -#endif /* #ifndef GLOBALS_H_INCLUDED */ +/* these are required by some dynamically loaded modules */ diff --git a/tests/testbench.h b/tests/testbench.h new file mode 100644 index 00000000..6f26724a --- /dev/null +++ b/tests/testbench.h @@ -0,0 +1,102 @@ +/* Defines for a rsyslog standard testbench application. + * + * Work begun 2008-06-13 by Rainer Gerhards (written from scratch) + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include <stdlib.h> + +/* everything we need to begin a testbench */ +#define MODULE_TYPE_TESTBENCH \ +/* definitions for objects we access */ \ +DEFobjCurrIf(obj) \ +\ +static rsRetVal doInit(void); \ +static rsRetVal doTest(void); \ +static rsRetVal doExit(void); \ +\ +/* Below is the driver, which is always the same */ \ +int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[]) \ +{ \ + DEFiRet; \ + CHKiRet(doInit()); \ + CHKiRet(doTest()); \ + CHKiRet(doExit()); \ +finalize_it: \ + printf("test returns iRet %d\n", iRet); \ + RETiRet; \ +} + + +/* Initialize everything (most importantly the runtime objects) for the test. The framework + * initializes the global runtime, user must add those objects that it needs additionally. + */ +#define BEGINInit \ +static rsRetVal doInit(void) \ +{ \ + DEFiRet; \ + char *pErrObj; /* tells us which object failed if that happens */ \ + putenv("RSYSLOG_MODDIR=../runtime/.libs/"); /* this is a bit hackish... */ \ + \ + dbgClassInit(); \ + /* Intialize the runtime system */ \ + pErrObj = "rsyslog runtime"; /* set in case the runtime errors before setting an object */ \ + CHKiRet(rsrtInit(&pErrObj, &obj)); \ + +#define CODESTARTInit + +#define ENDInit \ +finalize_it: \ + if(iRet != RS_RET_OK) { \ + printf("failure occured during init of object '%s'\n", pErrObj); \ + } \ + \ + RETiRet; \ +} + + + +/* Carry out the actual test... + */ +#define BEGINTest \ +rsRetVal doTest(void) \ +{ \ + DEFiRet; + +#define CODESTARTTest + +#define ENDTest \ + RETiRet; \ +} + + +/* De-init everything (most importantly the runtime objects) for the test. */ +#define BEGINExit \ +rsRetVal doExit(void) \ +{ \ + DEFiRet; \ + CHKiRet(rsrtExit()); + +#define CODESTARTExit + +#define ENDExit \ +finalize_it: \ + RETiRet; \ +} @@ -33,7 +33,7 @@ #include <assert.h> #include "rsyslog.h" -#include "syslogd.h" +#include "dirty.h" #include "linkedlist.h" #include "threads.h" @@ -23,16 +23,15 @@ #ifndef THREADS_H_INCLUDED #define THREADS_H_INCLUDED - /* the thread object */ -typedef struct thrdInfo { +struct thrdInfo { pthread_mutex_t *mutTermOK; /* Is it ok to terminate that thread now? */ int bIsActive; /* Is thread running? */ int bShallStop; /* set to 1 if the thread should be stopped ? */ rsRetVal (*pUsrThrdMain)(struct thrdInfo*); /* user thread main to be called in new thread */ rsRetVal (*pAfterRun)(struct thrdInfo*); /* cleanup function */ pthread_t thrdID; -} thrdInfo_t; +}; /* prototypes */ rsRetVal thrdExit(void); diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 00000000..b2b7a8ca --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,29 @@ +sbin_PROGRAMS = +man_MANS = rsyslogd.8 rsyslog.conf.5 + +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 \ + \ + ../dirty.h + +rsyslogd_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) +rsyslogd_LDADD = $(zlib_libs) $(pthreads_libs) $(rsrt_libs) +rsyslogd_LDFLAGS = -export-dynamic + +EXTRA_DIST = $(man_MANS) diff --git a/tools/gnutls/cert-gen-selfsigned b/tools/gnutls/cert-gen-selfsigned new file mode 100755 index 00000000..e1c25386 --- /dev/null +++ b/tools/gnutls/cert-gen-selfsigned @@ -0,0 +1,6 @@ +#/bin/sh +# generates a self-signed certificate and key suitable for use with rsyslog +# 2008-05-08, rgerhards +# TODO: make this a robust shell script +certtool --generate-privkey --outfile $1-key.pem +certtool --generate-self-signed --load-privkey $1-key.pem --outfile $1-cert.pem diff --git a/tools/gnutls/cert-show-fingerprint b/tools/gnutls/cert-show-fingerprint new file mode 100755 index 00000000..f61c6840 --- /dev/null +++ b/tools/gnutls/cert-show-fingerprint @@ -0,0 +1,6 @@ +#/bin/sh +# must be called with the certificate file as first parameter. Displays all +# fingerprints for the first certificate. +# 2008-05-08, rgerhards +# TODO: make this a robust shell script +certtool -i < $1|grep Fingerprint diff --git a/iminternal.c b/tools/iminternal.c index 60460a99..60460a99 100644 --- a/iminternal.c +++ b/tools/iminternal.c diff --git a/iminternal.h b/tools/iminternal.h index 8dc0f171..8dc0f171 100644 --- a/iminternal.h +++ b/tools/iminternal.h diff --git a/omdiscard.c b/tools/omdiscard.c index f13144e8..f13144e8 100644 --- a/omdiscard.c +++ b/tools/omdiscard.c diff --git a/omdiscard.h b/tools/omdiscard.h index 116308a4..116308a4 100644 --- a/omdiscard.h +++ b/tools/omdiscard.h diff --git a/omfile.c b/tools/omfile.c index 6a53a723..06875fe4 100644 --- a/omfile.c +++ b/tools/omfile.c @@ -85,6 +85,7 @@ static int bEnableSync = 0;/* enable syncing of files (no dash in front of pathn static uchar *pszTplName = NULL; /* name of the default template to use */ /* end globals for default values */ + typedef struct _instanceData { uchar f_fname[MAXFNAME];/* file or template name (display only) */ short fd; /* file descriptor for (current) file */ @@ -160,14 +161,14 @@ rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal) snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), "DynaFileCacheSize must be greater 0 (%d given), changed to 1.", iNewVal); errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg); iRet = RS_RET_VAL_OUT_OF_RANGE; iNewVal = 1; } else if(iNewVal > 10000) { snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), "DynaFileCacheSize maximum is 10,000 (%d given), changed to 10,000.", iNewVal); errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg); iRet = RS_RET_VAL_OUT_OF_RANGE; iNewVal = 10000; } @@ -220,7 +221,7 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR snprintf(errMsg, sizeof(errMsg)/sizeof(char), "outchannel '%s' not found - ignoring action line", szBuf); - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_NOT_FOUND, "%s", errMsg); ABORT_FINALIZE(RS_RET_NOT_FOUND); } @@ -231,7 +232,7 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR snprintf(errMsg, sizeof(errMsg)/sizeof(char), "outchannel '%s' has no file name template - ignoring action line", szBuf); - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_ERR, "%s", errMsg); ABORT_FINALIZE(RS_RET_ERR); } @@ -280,7 +281,6 @@ int resolveFileSizeLimit(instanceData *pData) */ if((pCmd = (uchar*)strdup((char*)pData->f_sizeLimitCmd)) == NULL) { /* there is not much we can do - we make syslogd close the file in this case */ - glblHadMemShortage = 1; return 1; } @@ -480,7 +480,6 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg /* we need to allocate memory for the cache structure */ pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry)); if(pCache[iFirstFree] == NULL) { - glblHadMemShortage = TRUE; dbgprintf("prepareDynfile(): could not alloc mem, discarding this request\n"); return -1; } @@ -498,7 +497,7 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg if(iMsgOpts & INTERNAL_MSG) dbgprintf("Could not open dynaFile, discarding message\n"); else - errmsg.LogError(NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", (char*)newFileName); + errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", (char*)newFileName); dynaFileDelCacheEntry(pCache, iFirstFree, 1); pData->iCurrElt = -1; return -1; @@ -555,14 +554,14 @@ again: "no longer writing to file %s; grown beyond configured file size of %lld bytes, actual size %lld - configured command did not resolve situation", pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize); errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "%s", errMsg); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); } else { snprintf(errMsg, sizeof(errMsg), "file %s had grown beyond configured file size of %lld bytes, actual size was %lld - configured command resolved situation", pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize); errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); + errmsg.LogError(0, NO_ERRCODE, "%s", errMsg); } } } @@ -596,7 +595,7 @@ again: pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_NOCTTY); if (pData->fd < 0) { iRet = RS_RET_DISABLE_ACTION; - errmsg.LogError(NO_ERRCODE, "%s", pData->f_fname); + errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname); } else { untty(); goto again; @@ -604,7 +603,7 @@ again: } else { iRet = RS_RET_DISABLE_ACTION; errno = e; - errmsg.LogError(NO_ERRCODE, "%s", pData->f_fname); + errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname); } } else if (pData->bSyncFile) { fsync(pData->fd); @@ -768,14 +767,14 @@ CODESTARTparseSelectorAct if ( pData->fd < 0 ){ pData->fd = -1; dbgprintf("Error opening log file: %s\n", pData->f_fname); - errmsg.LogError(NO_ERRCODE, "%s", pData->f_fname); + errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname); break; } if (isatty(pData->fd)) { pData->fileType = eTypeTTY; untty(); } - if (strcmp((char*) p, ctty) == 0) + if (strcmp((char*) p, _PATH_CONSOLE) == 0) pData->fileType = eTypeCONSOLE; break; default: @@ -841,6 +840,5 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(regCfSysLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit -/* - * vi:set ai: +/* vi:set ai: */ diff --git a/omfile.h b/tools/omfile.h index 03e081f3..03e081f3 100644 --- a/omfile.h +++ b/tools/omfile.h diff --git a/tools/omfwd.c b/tools/omfwd.c new file mode 100644 index 00000000..1b617ee1 --- /dev/null +++ b/tools/omfwd.c @@ -0,0 +1,714 @@ +/* omfwd.c + * This is the implementation of the build-in forwarding output module. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c) + * This file is under development and has not yet arrived at being fully + * self-contained and a real object. So far, it is mostly an excerpt + * of the "old" message code without any modifications. However, it + * helps to have things at the right place one we go to the meat of it. + * + * Copyright 2007 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 "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fnmatch.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#ifdef USE_NETZIP +#include <zlib.h> +#endif +#include <pthread.h> +#include "syslogd.h" +#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" +#include "tcpclt.h" +#include "cfsysline.h" +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" + +MODULE_TYPE_OUTPUT + +/* internal structures + */ +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 */ + uchar *pszStrmDrvr; + uchar *pszStrmDrvrAuthMode; + permittedPeers_t *pPermPeers; + int iStrmDrvrMode; + char *f_hname; + int *pSockArray; /* sockets to use for UDP */ + 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; + int protocol; +# define FORW_UDP 0 +# define FORW_TCP 1 + /* following fields for TCP-based delivery */ + tcpclt_t *pTCPClt; /* our tcpclt object */ +} instanceData; + +/* config data */ +static uchar *pszTplName = NULL; /* name of the default template to use */ +static uchar *pszStrmDrvr = NULL; /* name of the stream driver to use */ +static short iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ +static short bResendLastOnRecon = 0; /* should the last message be re-sent on a successful reconnect? */ +static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ + +static permittedPeers_t *pPermPeers = NULL; + +/* get the syslog forward port from selector_t. The passed in + * struct must be one that is setup for forwarding. + * rgerhards, 2007-06-28 + * 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 *getFwdPt(instanceData *pData) +{ + assert(pData != NULL); + if(pData->port == NULL) + return("514"); + else + return(pData->port); +} + + +/* destruct the TCP helper objects + * This, for example, is needed after something went wrong. + * This function is void because it "can not" fail. + * rgerhards, 2008-06-04 + */ +static inline void +DestructTCPInstanceData(instanceData *pData) +{ + assert(pData != NULL); + if(pData->pNetstrm != NULL) + netstrm.Destruct(&pData->pNetstrm); + if(pData->pNS != NULL) + netstrms.Destruct(&pData->pNS); +} + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + 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 */ + DestructTCPInstanceData(pData); + if(pData->pSockArray != NULL) + net.closeUDPListenSockets(pData->pSockArray); + + if(pData->protocol == FORW_TCP) { + tcpclt.Destruct(&pData->pTCPClt); + } + + if(pData->f_hname != NULL) + free(pData->f_hname); + if(pData->pszStrmDrvr != NULL) + free(pData->pszStrmDrvr); + if(pData->pszStrmDrvrAuthMode != NULL) + free(pData->pszStrmDrvrAuthMode); + if(pData->pPermPeers != NULL) + net.DestructPermittedPeers(&pData->pPermPeers); +ENDfreeInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + printf("%s", pData->f_hname); +ENDdbgPrintInstInfo + + +/* Send a message via UDP + * rgehards, 2007-12-20 + */ +static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len) +{ + DEFiRet; + struct addrinfo *r; + int i; + unsigned lsent = 0; + int bSendSuccess; + + if(pData->pSockArray != NULL) { + /* we need to track if we have success sending to the remote + * peer. Success is indicated by at least one sendto() call + * 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 + */ + bSendSuccess = FALSE; + for (r = pData->f_addr; r; r = r->ai_next) { + for (i = 0; i < *pData->pSockArray; i++) { + lsent = sendto(pData->pSockArray[i+1], msg, len, 0, r->ai_addr, r->ai_addrlen); + if (lsent == len) { + bSendSuccess = TRUE; + break; + } else { + int eno = errno; + char errStr[1024]; + dbgprintf("sendto() error: %d = %s.\n", + eno, rs_strerror_r(eno, errStr, sizeof(errStr))); + } + } + if (lsent == len && !send_to_all) + break; + } + /* finished looping */ + if (bSendSuccess == FALSE) { + dbgprintf("error forwarding via udp, suspending\n"); + iRet = RS_RET_SUSPENDED; + } + } + + RETiRet; +} + + +/* set the permitted peers -- rgerhards, 2008-05-19 + */ +static rsRetVal +setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID) +{ + DEFiRet; + CHKiRet(net.AddPermittedPeer(&pPermPeers, pszID)); + free(pszID); /* no longer needed, but we must free it as of interface def */ +finalize_it: + RETiRet; +} + + + +/* CODE FOR SENDING TCP MESSAGES */ + + +/* Send a frame via plain TCP protocol + * rgerhards, 2007-12-28 + */ +static rsRetVal TCPSendFrame(void *pvData, char *msg, size_t len) +{ + DEFiRet; + ssize_t lenSend; + instanceData *pData = (instanceData *) pvData; + + lenSend = len; + netstrm.CheckConnection(pData->pNetstrm); /* hack for plain tcp syslog - see ptcp driver for details */ + CHKiRet(netstrm.Send(pData->pNetstrm, (uchar*)msg, &lenSend)); + dbgprintf("TCP sent %ld bytes, requested %ld\n", (long) lenSend, (long) 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 + */ + dbgprintf("message not completely (tcp)send, ignoring %ld\n", (long) lenSend); + usleep(1000); /* experimental - might be benefitial in this situation */ + /* TODO: we need to revisit this code -- rgerhards, 2007-12-28 */ + } + +finalize_it: + RETiRet; +} + + +/* This function is called immediately before a send retry is attempted. + * It shall clean up whatever makes sense. + * rgerhards, 2007-12-28 + */ +static rsRetVal TCPSendPrepRetry(void *pvData) +{ + DEFiRet; + instanceData *pData = (instanceData *) pvData; + + assert(pData != NULL); + DestructTCPInstanceData(pData); + RETiRet; +} + + +/* initializes everything so that TCPSend can work. + * rgerhards, 2007-12-28 + */ +static rsRetVal TCPSendInit(void *pvData) +{ + DEFiRet; + instanceData *pData = (instanceData *) pvData; + + assert(pData != NULL); + if(pData->pNetstrm == NULL) { + CHKiRet(netstrms.Construct(&pData->pNS)); + /* the stream driver must be set before the object is finalized! */ + CHKiRet(netstrms.SetDrvrName(pData->pNS, pszStrmDrvr)); + 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.SetDrvrMode(pData->pNetstrm, pData->iStrmDrvrMode)); + /* now set optional params, but only if they were actually configured */ + if(pData->pszStrmDrvrAuthMode != NULL) { + CHKiRet(netstrm.SetDrvrAuthMode(pData->pNetstrm, pData->pszStrmDrvrAuthMode)); + } + if(pData->pPermPeers != NULL) { + CHKiRet(netstrm.SetDrvrPermPeers(pData->pNetstrm, pData->pPermPeers)); + } + /* params set, now connect */ + CHKiRet(netstrm.Connect(pData->pNetstrm, glbl.GetDefPFFamily(), + (uchar*)getFwdPt(pData), (uchar*)pData->f_hname)); + } + +finalize_it: + if(iRet != RS_RET_OK) { + DestructTCPInstanceData(pData); + } + + RETiRet; +} + + +/* try to resume connection if it is not ready + * rgerhards, 2007-08-02 + */ +static rsRetVal doTryResume(instanceData *pData) +{ + int iErr; + struct addrinfo *res; + struct addrinfo hints; + DEFiRet; + + 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 requires this */ + hints.ai_flags = AI_NUMERICSERV; + hints.ai_family = glbl.GetDefPFFamily(); + hints.ai_socktype = SOCK_DGRAM; + if((iErr = (getaddrinfo(pData->f_hname, getFwdPt(pData), &hints, &res))) != 0) { + dbgprintf("could not get addrinfo for hostname '%s':'%s': %d%s\n", + pData->f_hname, getFwdPt(pData), iErr, gai_strerror(iErr)); + 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; + } + iRet = RS_RET_SUSPENDED; + } + + RETiRet; +} + + +BEGINtryResume +CODESTARTtryResume + iRet = doTryResume(pData); +ENDtryResume + +BEGINdoAction + char *psz; /* temporary buffering */ + register unsigned l; +CODESTARTdoAction + 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! */ + } + ++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"); + DestructTCPInstanceData(pData); + iRet = RS_RET_SUSPENDED; + } + } +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; + CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME)); + CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME)); + CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME)); + +finalize_it: + RETiRet; +} + + +BEGINparseSelectorAct + uchar *q; + int i; + int bErr; + rsRetVal localRet; + struct addrinfo; + TCPFRAMINGMODE tcp_framing = TCP_FRAMING_OCTET_STUFFING; +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + 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(0, localRet, "could not activate network stream modules for TCP " + "(internal error %d) - are modules missing?", localRet); + ABORT_FINALIZE(localRet); + } + 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 */ + pData->compressionLevel = iLevel; + } else { + errmsg.LogError(0, NO_ERRCODE, "Invalid compression level '%c' specified in " + "forwardig action - NOT turning on compression.", + *p); + } +# else + errmsg.LogError(0, 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(0, 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(0, 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 + */ + if(*p == '[') { /* everything is hostname upto ']' */ + ++p; /* skip '[' */ + for(q = p ; *p && *p != ']' ; ++p) + /* JUST SKIP */; + if(*p == ']') { + *p = '\0'; /* trick to obtain hostname (later)! */ + ++p; /* eat it */ + } + } else { /* traditional view of hostname */ + for(q = p ; *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(0, 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'; + } + } + + /* now skip to template */ + bErr = 0; + while(*p && *p != ';' && *p != '#' && !isspace((int) *p)) + ++p; /*JUST SKIP*/ + + /* TODO: make this if go away! */ + if(*p == ';' || *p == '#' || isspace(*p)) { + uchar cTmp = *p; + *p = '\0'; /* trick to obtain hostname (later)! */ + CHKmalloc(pData->f_hname = strdup((char*) q)); + *p = cTmp; + } else { + CHKmalloc(pData->f_hname = strdup((char*) q)); + } + + /* process template */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, + (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName)); + + if(pData->protocol == FORW_TCP) { + /* create our tcpclt */ + CHKiRet(tcpclt.Construct(&pData->pTCPClt)); + CHKiRet(tcpclt.SetResendLastOnRecon(pData->pTCPClt, bResendLastOnRecon)); + /* 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)); + pData->iStrmDrvrMode = iStrmDrvrMode; + if(pszStrmDrvr != NULL) + CHKmalloc(pData->pszStrmDrvr = (uchar*)strdup((char*)pszStrmDrvr)); + if(pszStrmDrvrAuthMode != NULL) + CHKmalloc(pData->pszStrmDrvrAuthMode = + (uchar*)strdup((char*)pszStrmDrvrAuthMode)); + if(pPermPeers != NULL) { + pData->pPermPeers = pPermPeers; + pPermPeers = NULL; + } + } + +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +/* a common function to free our configuration variables - used both on exit + * and on $ResetConfig processing. -- rgerhards, 2008-05-16 + */ +static void +freeConfigVars(void) +{ + if(pszTplName != NULL) { + free(pszTplName); + pszTplName = NULL; + } + if(pszStrmDrvr != NULL) { + free(pszStrmDrvr); + pszStrmDrvr = NULL; + } + if(pszStrmDrvrAuthMode != NULL) { + free(pszStrmDrvrAuthMode); + pszStrmDrvrAuthMode = NULL; + } + if(pPermPeers != NULL) { + free(pPermPeers); + } +} + + +BEGINmodExit +CODESTARTmodExit + /* release what we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); + objRelease(net, LM_NET_FILENAME); + objRelease(netstrm, LM_NETSTRMS_FILENAME); + objRelease(netstrms, LM_NETSTRMS_FILENAME); + objRelease(tcpclt, LM_TCPCLT_FILENAME); + + freeConfigVars(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +ENDqueryEtryPt + + +/* Reset config variables for this module to default values. + * rgerhards, 2008-03-28 + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + freeConfigVars(); + + /* we now must reset all non-string values */ + iStrmDrvrMode = 0; + bResendLastOnRecon = 0; + + return RS_RET_OK; +} + + +BEGINmodInit(Fwd) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(net,LM_NET_FILENAME)); + + CHKiRet(regCfSysLineHdlr((uchar *)"actionforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriver", 0, eCmdHdlrGetWord, NULL, &pszStrmDrvr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdrivermode", 0, eCmdHdlrInt, NULL, &iStrmDrvrMode, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverauthmode", 0, eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverpermittedpeer", 0, eCmdHdlrGetWord, setPermittedPeer, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendresendlastmsgonreconnect", 0, eCmdHdlrBinary, NULL, &bResendLastOnRecon, NULL)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit + +/* vim:set ai: + */ diff --git a/omshell.c b/tools/omshell.c index 2176c101..7b815869 100644 --- a/omshell.c +++ b/tools/omshell.c @@ -92,7 +92,7 @@ CODESTARTdoAction */ dbgprintf("\n"); if(execProg((uchar*) pData->progName, 1, ppString[0]) == 0) - errmsg.LogError(NO_ERRCODE, "Executing program '%s' failed", (char*)pData->progName); + errmsg.LogError(0, NO_ERRCODE, "Executing program '%s' failed", (char*)pData->progName); ENDdoAction diff --git a/omshell.h b/tools/omshell.h index 3061ad07..3061ad07 100644 --- a/omshell.h +++ b/tools/omshell.h diff --git a/omusrmsg.c b/tools/omusrmsg.c index 42d3291d..830bbc87 100644 --- a/omusrmsg.c +++ b/tools/omusrmsg.c @@ -11,7 +11,18 @@ * of the "old" message code without any modifications. However, it * helps to have things at the right place one we go to the meat of it. * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. + * + * rgerhards, 2008-07-04 (happy Independence Day!): rsyslog inherited the + * wall functionality from sysklogd. Sysklogd was single-threaded and could + * not afford to spent a lot of time inside a single action. Thus, it forked + * off a new process to do the wall. In rsyslog, however, this creates some + * grief with the threading model. Also, we do not really need to de-couple + * processing, because we have ample ways to do it in rsyslog. Plus, the + * default main message queue will care for a somewhat longer execution time. + * So in short, the real fix to the problem is an architecture change. From + * now on, we will not fork off a new process but rather do the notification + * within the current one. This also reduces system overhead. * * This file is part of rsyslog. * @@ -43,7 +54,7 @@ #include <unistd.h> #include <sys/uio.h> #include <sys/stat.h> -#include <setjmp.h> +#include <errno.h> #if HAVE_FCNTL_H #include <fcntl.h> #else @@ -106,13 +117,6 @@ CODESTARTdbgPrintInstInfo ENDdbgPrintInstInfo -static jmp_buf ttybuf; - -static void endtty() -{ - longjmp(ttybuf, 1); -} - /** * BSD setutent/getutent() replacement routines * The following routines emulate setutent() and getutent() under @@ -148,8 +152,7 @@ void endutent(void) #endif /* #ifdef OS_BSD */ -/* - * WALLMSG -- Write a message to the world at large +/* WALLMSG -- Write a message to the world at large * * Write the specified message to either the entire * world, or a list of approved users. @@ -158,108 +161,86 @@ void endutent(void) * Tue May 4 16:52:01 CEST 2004: Solar Designer <solar@openwall.com> * Adjust the size of a variable to prevent a buffer overflow * should _PATH_DEV ever contain something different than "/dev/". + * rgerhards, 2008-07-04: changing the function to no longer use fork() but + * continue run on its thread instead. */ static rsRetVal wallmsg(uchar* pMsg, instanceData *pData) { + uchar szErr[512]; char p[sizeof(_PATH_DEV) + UNAMESZ]; register int i; + int errnoSave; int ttyf; - static int reenter = 0; + int wrRet; struct utmp ut; struct utmp *uptr; - struct sigaction sigAct; + struct stat statb; + DEFiRet; assert(pMsg != NULL); - if (reenter++) - return RS_RET_OK; - /* open the user login file */ setutent(); - /* - * Might as well fork instead of using nonblocking I/O - * and doing notty(). - */ - if (fork() == 0) { - memset(&sigAct, 0, sizeof(sigAct)); - sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = SIG_DFL; - sigaction(SIGTERM, &sigAct, NULL); - alarm(0); - -# ifdef SIGTTOU - sigAct.sa_handler = SIG_DFL; - sigaction(SIGTERM, &sigAct, NULL); -# endif - /* It is save to call sigprocmask here, as we are now executing the child (no threads) */ - sigprocmask(SIG_SETMASK, &sigAct.sa_mask, NULL); - /* TODO: find a way to limit the max size of the message. hint: this - * should go into the template! - */ - - /* rgerhards 2005-10-24: HINT: this code might be run in a seperate thread - * instead of a seperate process once we have multithreading... - */ - - /* scan the user login file */ - while ((uptr = getutent())) { - memcpy(&ut, uptr, sizeof(ut)); - /* is this slot used? */ - if (ut.ut_name[0] == '\0') - continue; + /* scan the user login file */ + while((uptr = getutent())) { + memcpy(&ut, uptr, sizeof(ut)); + /* is this slot used? */ + if(ut.ut_name[0] == '\0') + continue; #ifndef OS_BSD - if (ut.ut_type != USER_PROCESS) - continue; + if(ut.ut_type != USER_PROCESS) + continue; #endif - if (!(strncmp (ut.ut_name,"LOGIN", 6))) /* paranoia */ - continue; - - /* should we send the message to this user? */ - if (pData->bIsWall == 0) { - for (i = 0; i < MAXUNAMES; i++) { - if (!pData->uname[i][0]) { - i = MAXUNAMES; - break; - } - if (strncmp(pData->uname[i], - ut.ut_name, UNAMESZ) == 0) - break; + if(!(strncmp (ut.ut_name,"LOGIN", 6))) /* paranoia */ + continue; + + /* should we send the message to this user? */ + if(pData->bIsWall == 0) { + for(i = 0; i < MAXUNAMES; i++) { + if(!pData->uname[i][0]) { + i = MAXUNAMES; + break; } - if (i >= MAXUNAMES) - continue; + if(strncmp(pData->uname[i], ut.ut_name, UNAMESZ) == 0) + break; } + if(i == MAXUNAMES) /* user not found? */ + continue; /* on to next user! */ + } - /* compute the device name */ - strcpy(p, _PATH_DEV); - strncat(p, ut.ut_line, UNAMESZ); - - if (setjmp(ttybuf) == 0) { - sigAct.sa_handler = endtty; - sigaction(SIGALRM, &sigAct, NULL); - (void) alarm(15); - /* open the terminal */ - ttyf = open(p, O_WRONLY|O_NOCTTY); - if (ttyf >= 0) { - struct stat statb; - - if (fstat(ttyf, &statb) == 0 && - (statb.st_mode & S_IWRITE)) { - (void) write(ttyf, pMsg, strlen((char*)pMsg)); - } - close(ttyf); - ttyf = -1; + /* compute the device name */ + strcpy(p, _PATH_DEV); + strncat(p, ut.ut_line, UNAMESZ); + + /* we must be careful when writing to the terminal. A terminal may block + * (for example, a user has pressed <ctl>-s). In that case, we can not + * wait indefinitely. So we need to use non-blocking I/O. In case we would + * block, we simply do not send the message, because that's the best we can + * do. -- rgerhards, 2008-07-04 + */ + + /* open the terminal */ + if((ttyf = open(p, O_WRONLY|O_NOCTTY|O_NONBLOCK)) >= 0) { + if(fstat(ttyf, &statb) == 0 && (statb.st_mode & S_IWRITE)) { + wrRet = write(ttyf, pMsg, strlen((char*)pMsg)); + if(Debug && wrRet == -1) { + /* we record the state to the debug log */ + errnoSave = errno; + rs_strerror_r(errno, (char*)szErr, sizeof(szErr)); + dbgprintf("write to terminal '%s' failed with [%d]:%s\n", + p, errnoSave, szErr); } } - (void) alarm(0); + close(ttyf); + ttyf = -1; } - exit(0); /* "good" exit - this terminates the child forked just for message delivery */ } + /* close the user login file */ endutent(); - reenter = 0; - return RS_RET_OK; + RETiRet; } @@ -279,30 +260,24 @@ BEGINparseSelectorAct int i; CODESTARTparseSelectorAct CODE_STD_STRING_REQUESTparseSelectorAct(1) + /* User names must begin with a gnu e-regex: + * [a-zA-Z0-9_.] + * plus '*' for wall + */ + if(!*p || !((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') + || (*p >= '0' && *p <= '9') || *p == '_' || *p == '.' || *p == '*')) + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); - /* User names must begin with a gnu e-regex: - * [a-zA-Z0-9_.] - * plus '*' for wall - */ - if (!*p || !((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') - || (*p >= '0' && *p <= '9') || *p == '_' || *p == '.' || *p == '*')) - ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); - - if((iRet = createInstance(&pData)) != RS_RET_OK) - goto finalize_it; - + CHKiRet(createInstance(&pData)); if(*p == '*') { /* wall */ dbgprintf("write-all"); ++p; /* eat '*' */ pData->bIsWall = 1; /* write to all users */ - if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " WallFmt")) - != RS_RET_OK) - goto finalize_it; + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " WallFmt")); } else { - /* everything else beginning with the regex above - * is currently treated as a user name - * TODO: is this portable? + /* everything else beginning with the regex above + * is currently treated as a user name -- TODO: is this portable? */ dbgprintf("users: %s\n", p); /* ASP */ pData->bIsWall = 0; /* write to individual users */ @@ -322,7 +297,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) * TODO: we need to handle the case where i >= MAXUNAME! */ if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*)" StdUsrMsgFmt")) - != RS_RET_OK) + != RS_RET_OK) goto finalize_it; } CODE_STD_FINALIZERparseSelectorAct @@ -347,6 +322,5 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); ENDmodInit -/* - * vi:set ai: +/* vim:set ai: */ diff --git a/omusrmsg.h b/tools/omusrmsg.h index 52e780f7..52e780f7 100644 --- a/omusrmsg.h +++ b/tools/omusrmsg.h diff --git a/pidfile.c b/tools/pidfile.c index 2be13da6..2be13da6 100644 --- a/pidfile.c +++ b/tools/pidfile.c diff --git a/pidfile.h b/tools/pidfile.h index 40be9069..40be9069 100644 --- a/pidfile.h +++ b/tools/pidfile.h diff --git a/rsyslog.conf.5 b/tools/rsyslog.conf.5 index 0a2422c6..0a2422c6 100644 --- a/rsyslog.conf.5 +++ b/tools/rsyslog.conf.5 diff --git a/rsyslogd.8 b/tools/rsyslogd.8 index 91f2016e..91f2016e 100644 --- a/rsyslogd.8 +++ b/tools/rsyslogd.8 diff --git a/syslogd.c b/tools/syslogd.c index 6855a8a5..439ca303 100644 --- a/syslogd.c +++ b/tools/syslogd.c @@ -129,6 +129,8 @@ #include <zlib.h> #endif +#include <netdb.h> + #include "pidfile.h" #include "srUtils.h" #include "stringbuf.h" @@ -140,7 +142,6 @@ #include "msg.h" #include "modules.h" #include "action.h" -#include "tcpsyslog.h" #include "iminternal.h" #include "cfsysline.h" #include "omshell.h" @@ -151,21 +152,15 @@ #include "threads.h" #include "queue.h" #include "stream.h" -#include "wti.h" -#include "wtp.h" -#include "expr.h" -#include "ctok.h" #include "conf.h" -#include "vmop.h" -#include "vmstk.h" #include "vm.h" -#include "vmprg.h" #include "errmsg.h" #include "datetime.h" #include "sysvar.h" /* definitions for objects we access */ DEFobjCurrIf(obj) +DEFobjCurrIf(glbl) DEFobjCurrIf(datetime) DEFobjCurrIf(conf) DEFobjCurrIf(expr) @@ -243,25 +238,17 @@ static rsRetVal GlobalClassExit(void); # define _PATH_DEV "/dev/" #endif -#ifndef _PATH_CONSOLE -#define _PATH_CONSOLE "/dev/console" -#endif - #ifndef _PATH_TTY #define _PATH_TTY "/dev/tty" #endif static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */ static char *PidFile = _PATH_LOGPID; /* read-only after startup */ -char ctty[] = _PATH_CONSOLE; /* this is read-only; used by omfile -- TODO: remove that dependency */ static pid_t myPid; /* our pid for use in self-generated messages, e.g. on startup */ /* mypid is read-only after the initial fork() */ static int restart = 0; /* do restart (config read) - multithread safe */ -int glblHadMemShortage = 0; /* indicates if we had memory shortage some time during the run */ - - static int bParseHOSTNAMEandTAG = 1; /* global config var: should the hostname and tag be * parsed inside message - rgerhards, 2006-03-13 */ static int bFinished = 0; /* used by termination signal handler, read-only except there @@ -296,25 +283,16 @@ int iCompatibilityMode = 0; /* version we should be compatible with; 0 means sy static int bDebugPrintTemplateList = 1;/* output template list in debug mode? */ static int bDebugPrintCfSysLineHandlerList = 1;/* output cfsyslinehandler list in debug mode? */ static int bDebugPrintModuleList = 1;/* output module list in debug mode? */ -int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */ static uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */ static int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */ int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */ int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */ int iActExecOnceInterval = 0; /* execute action once every nn seconds */ -uchar *pszWorkDir = NULL;/* name of rsyslog's spool directory (without trailing slash) */ -uchar *glblModPath = NULL; /* module load path - only used during initial init, only settable via -M command line option */ /* end global config file state variables */ -uchar *LocalHostName;/* our hostname - read-only after startup */ -char *LocalDomain; /* our local domain name - read-only after startup */ int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ -int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both), set via cmdline */ int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ static int NoFork = 0; /* don't fork - don't run in daemon mode - read-only after startup */ -int DisableDNS = 0; /* don't look up IP addresses of remote messages */ -char **StripDomains = NULL;/* these domains may be stripped before writing logs - r/o after s.u., never touched by init */ -char **LocalHosts = NULL;/* these hosts are logged with their hostname - read-only after startup, never touched by init */ static int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode - is available * If the main queue is either not yet ready or not running in * queueing mode (mode DIRECT!), then this is set to 0. @@ -386,11 +364,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a bDebugPrintModuleList = 1; bEscapeCCOnRcv = 1; /* default is to escape control characters */ bReduceRepeatMsgs = 0; - bDropMalPTRMsgs = 0; - if(pszWorkDir != NULL) { - free(pszWorkDir); - pszWorkDir = NULL; - } if(pszMainMsgQFName != NULL) { free(pszMainMsgQFName); pszMainMsgQFName = NULL; @@ -418,11 +391,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a } - -int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */ - - /* 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_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\""; @@ -499,7 +469,6 @@ selectorConstruct(selector_t **ppThis) assert(ppThis != NULL); if((pThis = (selector_t*) calloc(1, sizeof(selector_t))) == NULL) { - glblHadMemShortage = 1; ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } CHKiRet(llInit(&pThis->llActList, actionDestruct, NULL, NULL)); @@ -629,19 +598,22 @@ void untty(void) * rgerhards, 2008-03-19: * I added an additional calling parameter to permit specifying the flow * control capability of the source. + * + * rgerhards, 2008-05-16: + * I added an additional calling parameter (hnameIP) to enable specifying the IP + * of a remote host. */ -rsRetVal printline(char *hname, char *msg, int bParseHost, int flags, flowControl_t flowCtlType) +rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int bParseHost, int flags, flowControl_t flowCtlType) { DEFiRet; - register char *p; + register uchar *p; int pri; msg_t *pMsg; - /* Now it is time to create the message object (rgerhards) - */ + /* Now it is time to create the message object (rgerhards) */ CHKiRet(msgConstruct(&pMsg)); MsgSetFlowControlType(pMsg, flowCtlType); - MsgSetRawMsg(pMsg, msg); + MsgSetRawMsg(pMsg, (char*)msg); pMsg->bParseHOSTNAME = bParseHost; /* test for special codes */ @@ -669,18 +641,19 @@ rsRetVal printline(char *hname, char *msg, int bParseHost, int flags, flowContro * being the local host). rgerhards 2004-11-16 */ if(bParseHost == 0) - MsgSetHOSTNAME(pMsg, hname); - MsgSetRcvFrom(pMsg, hname); + MsgSetHOSTNAME(pMsg, (char*)hname); + MsgSetRcvFrom(pMsg, (char*)hname); + CHKiRet(MsgSetRcvFromIP(pMsg, hnameIP)); /* rgerhards 2004-11-19: well, well... we've now seen that we * have the "hostname problem" also with the traditional Unix * message. As we like to emulate it, we need to add the hostname * to it. */ - if(MsgSetUxTradMsg(pMsg, p) != 0) + if(MsgSetUxTradMsg(pMsg, (char*)p) != 0) ABORT_FINALIZE(RS_RET_ERR); - logmsg(pMsg, flags | SYNC_FILE); + logmsg(pMsg, flags); finalize_it: RETiRet; @@ -720,22 +693,27 @@ finalize_it: * rgerhards, 2008-03-19: * I added an additional calling parameter to permit specifying the flow * control capability of the source. + * + * rgerhards, 2008-05-16: + * I added an additional calling parameter (hnameIP) to enable specifying the IP + * of a remote host. */ rsRetVal -parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType) +parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType) { DEFiRet; register int iMsg; - char *pMsg; - char *pData; - char *pEnd; - char tmpline[MAXLINE + 1]; + uchar *pMsg; + uchar *pData; + uchar *pEnd; + uchar tmpline[MAXLINE + 1]; # ifdef USE_NETZIP - char deflateBuf[MAXLINE + 1]; + uchar deflateBuf[MAXLINE + 1]; uLongf iLenDefBuf; # endif assert(hname != NULL); + assert(hnameIP != NULL); assert(msg != NULL); assert(len >= 0); @@ -795,7 +773,7 @@ parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags * rgerhards, 2006-12-07 */ if(ret != Z_OK) { - errmsg.LogError(NO_ERRCODE, "Uncompression of a message failed with return code %d " + errmsg.LogError(0, NO_ERRCODE, "Uncompression of a message failed with return code %d " "- enable debug logging if you need further information. " "Message ignored.", ret); FINALIZE; /* unconditional exit, nothing left to do... */ @@ -808,7 +786,7 @@ parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags * tell the user we can not accept it. */ if(len > 0 && *msg == 'z') { - errmsg.LogError(NO_ERRCODE, "Received a compressed message, but rsyslogd does not have compression " + errmsg.LogError(0, NO_ERRCODE, "Received a compressed message, but rsyslogd does not have compression " "support enabled. The message will be ignored."); FINALIZE; } @@ -821,7 +799,7 @@ parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags */ if(iMsg == MAXLINE) { *(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */ - printline(hname, tmpline, bParseHost, flags, flowCtlType); + printline(hname, hnameIP, tmpline, bParseHost, flags, flowCtlType); } else { /* This case in theory never can happen. If it happens, we have * a logic error. I am checking for it, because if I would not, @@ -873,12 +851,25 @@ parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags *(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */ /* typically, we should end up here! */ - printline(hname, tmpline, bParseHost, flags, flowCtlType); + printline(hname, hnameIP, tmpline, bParseHost, flags, flowCtlType); finalize_it: RETiRet; } + +/* this is a special function used to submit an error message. This + * function is also passed to the runtime library as the generic error + * message handler. -- rgerhards, 2008-04-17 + */ +rsRetVal +submitErrMsg(int iErr, uchar *msg) +{ + DEFiRet; + iRet = logmsgInternal(iErr, LOG_SYSLOG|LOG_ERR, msg, ADDDATE); + RETiRet; +} + /* rgerhards 2004-11-09: the following is a function that can be used * to log a message orginating from the syslogd itself. In sysklogd code, * this is done by simply calling logmsg(). However, logmsg() is changed in @@ -889,17 +880,28 @@ finalize_it: * think on the best way to do this. */ rsRetVal -logmsgInternal(int pri, char *msg, int flags) +logmsgInternal(int iErr, int pri, uchar *msg, int flags) { - DEFiRet; + uchar pszTag[33]; msg_t *pMsg; + DEFiRet; CHKiRet(msgConstruct(&pMsg)); - MsgSetUxTradMsg(pMsg, msg); - MsgSetRawMsg(pMsg, msg); - MsgSetHOSTNAME(pMsg, (char*)LocalHostName); - MsgSetRcvFrom(pMsg, (char*)LocalHostName); - MsgSetTAG(pMsg, "rsyslogd:"); + MsgSetUxTradMsg(pMsg, (char*)msg); + MsgSetRawMsg(pMsg, (char*)msg); + MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName()); + MsgSetRcvFrom(pMsg, (char*)glbl.GetLocalHostName()); + MsgSetRcvFromIP(pMsg, (uchar*)"127.0.0.1"); + /* check if we have an error code associated and, if so, + * adjust the tag. -- r5gerhards, 2008-06-27 + */ + if(iErr == NO_ERRCODE) { + MsgSetTAG(pMsg, "rsyslogd:"); + } else { + snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr); + pszTag[32] = '\0'; /* just to make sure... */ + MsgSetTAG(pMsg, (char*)pszTag); + } pMsg->iFacility = LOG_FAC(pri); pMsg->iSeverity = LOG_PRI(pri); pMsg->bParseHOSTNAME = 0; @@ -1604,7 +1606,7 @@ logmsg(msg_t *pMsg, int flags) assert(pMsg != NULL); assert(pMsg->pszUxTradMsg != NULL); msg = (char*) pMsg->pszUxTradMsg; - dbgprintf("logmsg: flags %x, from '%s', msg %s\n", flags, getRcvFrom(pMsg), msg); + dbgprintf("logmsg: flags %x, pri %s, from '%s', msg %s\n", flags, getPRI(pMsg), getRcvFrom(pMsg), msg); /* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have * a traditional syslog message or one formatted according to syslog-protocol. @@ -1758,7 +1760,7 @@ void legacyOptsHook(void) while(pThis != NULL) { if(pThis->line != NULL) { errno = 0; - errmsg.LogError(NO_ERRCODE, "Warning: backward compatibility layer added to following " + errmsg.LogError(0, NO_ERRCODE, "Warning: backward compatibility layer added to following " "directive to rsyslog.conf: %s", pThis->line); conf.cfsysline(pThis->line); } @@ -1868,14 +1870,10 @@ static void doDie(int sig) static void freeAllDynMemForTermination(void) { - if(pszWorkDir != NULL) - free(pszWorkDir); if(pszMainMsgQFName != NULL) free(pszMainMsgQFName); if(pModDir != NULL) free(pModDir); - if(LocalHostName != NULL) - free(LocalHostName); } @@ -1917,7 +1915,7 @@ die(int sig) "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"]" " exiting on signal %d.", (int) myPid, sig); errno = 0; - logmsgInternal(LOG_SYSLOG|LOG_INFO, buf, ADDDATE); + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, ADDDATE); } /* drain queue (if configured so) and stop main queue worker thread pool */ @@ -1941,8 +1939,6 @@ die(int sig) tplDeleteAll(); remove_pid(PidFile); - if(glblHadMemShortage) - dbgprintf("Had memory shortage at least once during the run.\n"); /* de-init some modules */ modExitIminternal(); @@ -2135,10 +2131,10 @@ static void dbgPrintInitInfo(void) dbgPrintCfSysLineHandlers(); dbgprintf("Messages with malicious PTR DNS Records are %sdropped.\n", - bDropMalPTRMsgs ? "" : "not "); + glbl.GetDropMalPTRMsgs() ? "" : "not "); dbgprintf("Control characters are %sreplaced upon reception.\n", - bEscapeCCOnRcv? "" : "not "); + bEscapeCCOnRcv? "" : "not "); if(bEscapeCCOnRcv) dbgprintf("Control character escape sequence prefix is '%c'.\n", @@ -2164,7 +2160,7 @@ static void dbgPrintInitInfo(void) setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100); setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1); */ - dbgprintf("Work Directory: '%s'.\n", pszWorkDir); + dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir()); } @@ -2281,19 +2277,19 @@ init(void) /* some checks */ if(iMainMsgQueueNumWorkers < 1) { - errmsg.LogError(NO_ERRCODE, "$MainMsgQueueNumWorkers must be at least 1! Set to 1.\n"); + errmsg.LogError(0, NO_ERRCODE, "$MainMsgQueueNumWorkers must be at least 1! Set to 1.\n"); iMainMsgQueueNumWorkers = 1; } if(MainMsgQueType == QUEUETYPE_DISK) { errno = 0; /* for logerror! */ - if(pszWorkDir == NULL) { - errmsg.LogError(NO_ERRCODE, "No $WorkDirectory specified - can not run main message queue in 'disk' mode. " + if(glbl.GetWorkDir() == NULL) { + errmsg.LogError(0, NO_ERRCODE, "No $WorkDirectory specified - can not run main message queue in 'disk' mode. " "Using 'FixedArray' instead.\n"); MainMsgQueType = QUEUETYPE_FIXED_ARRAY; } if(pszMainMsgQFName == NULL) { - errmsg.LogError(NO_ERRCODE, "No $MainMsgQueueFileName specified - can not run main message queue in " + errmsg.LogError(0, NO_ERRCODE, "No $MainMsgQueueFileName specified - can not run main message queue in " "'disk' mode. Using 'FixedArray' instead.\n"); MainMsgQueType = QUEUETYPE_FIXED_ARRAY; } @@ -2316,11 +2312,11 @@ init(void) /* ... set some properties ... */ # define setQPROP(func, directive, data) \ CHKiRet_Hdlr(func(pMsgQueue, data)) { \ - errmsg.LogError(NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ + errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ } # define setQPROPstr(func, directive, data) \ CHKiRet_Hdlr(func(pMsgQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \ - errmsg.LogError(NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ + errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ } setQPROP(queueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize); @@ -2372,7 +2368,7 @@ init(void) " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] restart", (int) myPid); - logmsgInternal(LOG_SYSLOG|LOG_INFO, bufStartUpMsg, ADDDATE); + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, ADDDATE); memset(&sigAct, 0, sizeof (sigAct)); sigemptyset(&sigAct.sa_mask); @@ -2404,7 +2400,7 @@ selectorAddList(selector_t *f) if(f != NULL) { CHKiRet(llGetNumElts(&f->llActList, &iActionCnt)); if(iActionCnt == 0) { - errmsg.LogError(NO_ERRCODE, "warning: selector line without actions will be discarded"); + errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded"); selectorDestruct(f); } else { /* successfully created an entry */ @@ -2451,7 +2447,7 @@ static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *psz MainMsgQueType = QUEUETYPE_DIRECT; dbgprintf("main message queue type set to DIRECT (no queueing at all)\n"); } else { - errmsg.LogError(NO_ERRCODE, "unknown mainmessagequeuetype parameter: %s", (char *) pszType); + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown mainmessagequeuetype parameter: %s", (char *) pszType); iRet = RS_RET_INVALID_PARAMS; } free(pszType); /* no longer needed */ @@ -2481,51 +2477,6 @@ void sighup_handler() } -/** - * getSubString - * - * Copy a string byte by byte until the occurrence - * of a given separator. - * - * \param ppSrc Pointer to a pointer of the source array of characters. If a - separator detected the Pointer points to the next char after the - separator. Except if the end of the string is dedected ('\n'). - Then it points to the terminator char. - * \param pDst Pointer to the destination array of characters. Here the substing - will be stored. - * \param DstSize Maximum numbers of characters to store. - * \param cSep Separator char. - * \ret int Returns 0 if no error occured. - * - * rgerhards, 2008-02-12: some notes are due... I will once again fix this function, this time - * so that it treats ' ' as a request for whitespace. But in general, the function and its callers - * should be changed over time, this is not really very good code... - */ -int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep) -{ - uchar *pSrc = *ppSrc; - int iErr = 0; /* 0 = no error, >0 = error */ - while((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0' && DstSize>1) { - *pDst++ = *(pSrc)++; - DstSize--; - } - /* check if the Dst buffer was to small */ - if ((cSep == ' ' ? !isspace(*pSrc) : *pSrc != cSep) && *pSrc != '\n' && *pSrc != '\0') { - dbgprintf("in getSubString, error Src buffer > Dst buffer\n"); - iErr = 1; - } - if (*pSrc == '\0' || *pSrc == '\n') - /* this line was missing, causing ppSrc to be invalid when it - * was returned in case of end-of-string. rgerhards 2005-07-29 - */ - *ppSrc = pSrc; - else - *ppSrc = pSrc+1; - *pDst = '\0'; - return iErr; -} - - /* this function pulls all internal messages from the buffer * and puts them into the processing engine. * We can only do limited error handling, as this would not @@ -2555,15 +2506,22 @@ mainloop(void) struct timeval tvSelectTimeout; BEGINfunc - while(!bFinished){ - /* first check if we have any internal messages queued and spit them out */ - /* TODO: do we need this any longer? I doubt it, but let's care about it - * later -- rgerhards, 2007-12-21 - */ - processImInternal(); + /* first check if we have any internal messages queued and spit them out. We used + * to do that on any loop iteration, but that is no longer necessry. The reason + * is that once we reach this point here, we always run on multiple threads and + * thus the main queue is properly initialized. -- rgerhards, 2008-06-09 + */ + processImInternal(); - /* this is now just a wait */ - tvSelectTimeout.tv_sec = TIMERINTVL; + while(!bFinished){ + /* this is now just a wait - please note that we do use a near-"eternal" + * timeout of 1 day if we do not have repeated message reduction turned on + * (which it is not by default). This enables us to help safe the environment + * by not unnecessarily awaking rsyslog on a regular tick (just think + * powertop, for example). In that case, we primarily wait for a signal, + * but a once-a-day wakeup should be quite acceptable. -- rgerhards, 2008-06-09 + */ + tvSelectTimeout.tv_sec = (bReduceRepeatMsgs == 1) ? TIMERINTVL : 86400 /*1 day*/; tvSelectTimeout.tv_usec = 0; select(1, NULL, NULL, NULL, &tvSelectTimeout); if(bFinished) @@ -2590,7 +2548,8 @@ mainloop(void) * for the time being, I think the remaining risk can be accepted. * rgerhards, 2008-01-10 */ - doFlushRptdMsgs(); + if(bReduceRepeatMsgs == 1) + doFlushRptdMsgs(); if(restart) { dbgprintf("\nReceived SIGHUP, reloading rsyslogd.\n"); @@ -2684,7 +2643,7 @@ static rsRetVal loadBuildInModules(void) * is that rsyslog will terminate if we can not register our built-in config commands. * This, I think, is the right thing to do. -- rgerhards, 2007-07-31 */ - CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL)); +// CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL)); @@ -2712,7 +2671,6 @@ static rsRetVal loadBuildInModules(void) CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeinterval", 0, eCmdHdlrInt, setActionResumeInterval, NULL, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"controlcharacterescapeprefix", 0, eCmdHdlrGetChar, NULL, &cCCEscapeChar, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"escapecontrolcharactersonreceive", 0, eCmdHdlrBinary, NULL, &bEscapeCCOnRcv, NULL)); - CHKiRet(regCfSysLineHdlr((uchar *)"dropmsgswithmaliciousdnsptrrecords", 0, eCmdHdlrBinary, NULL, &bDropMalPTRMsgs, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"droptrailinglfonreception", 0, eCmdHdlrBinary, NULL, &bDropTrailingLF, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"template", 0, eCmdHdlrCustomHandler, conf.doNameLine, (void*)DIR_TEMPLATE, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"outchannel", 0, eCmdHdlrCustomHandler, conf.doNameLine, (void*)DIR_OUTCHANNEL, NULL)); @@ -2791,6 +2749,8 @@ static void mainThread() */ /* initialize the build-in templates */ + pTmp = template_DebugFormat; + tplAddLine("RSYSLOG_DebugFormat", &pTmp); pTmp = template_SyslogProtocol23Format; tplAddLine("RSYSLOG_SyslogProtocol23Format", &pTmp); pTmp = template_FileFormat; /* new format for files with high-precision stamp */ @@ -2833,8 +2793,9 @@ static void mainThread() } -/* Method to initialize all global classes. +/* Method to initialize all global classes and use the objects that we need. * rgerhards, 2008-01-04 + * rgerhards, 2008-04-16: the actual initialization is now carried out by the runtime */ static rsRetVal InitGlobalClasses(void) @@ -2842,67 +2803,34 @@ InitGlobalClasses(void) DEFiRet; char *pErrObj; /* tells us which object failed if that happens (useful for troubleshooting!) */ - pErrObj = "obj"; - CHKiRet(objClassInit(NULL)); /* *THIS* *MUST* always be the first class initilizer being called! */ - CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ - /* the following classes were intialized by objClassInit() */ + /* Intialize the runtime system */ + pErrObj = "rsyslog runtime"; /* set in case the runtime errors before setting an object */ + CHKiRet(rsrtInit(&pErrObj, &obj)); + CHKiRet(rsrtSetErrLogger(submitErrMsg)); /* set out error handler */ + + /* Now tell the system which classes we need ourselfs */ + pErrObj = "glbl"; + CHKiRet(objUse(glbl, CORE_COMPONENT)); pErrObj = "errmsg"; CHKiRet(objUse(errmsg, CORE_COMPONENT)); pErrObj = "module"; CHKiRet(objUse(module, CORE_COMPONENT)); pErrObj = "var"; CHKiRet(objUse(var, CORE_COMPONENT)); - - /* initialize and use classes. We must be very careful with the order of events. Some - * classes use others and if we do not initialize them in the right order, we may end - * up with an invalid call. The most important thing that can happen is that an error - * is detected and needs to be logged, wich in turn requires a broader number of classes - * to be available. The solution is that we take care in the order of calls AND use a - * class immediately after it is initialized. And, of course, we load those classes - * first that we use ourselfs... -- rgerhards, 2008-03-07 - */ pErrObj = "datetime"; - CHKiRet(datetimeClassInit(NULL)); CHKiRet(objUse(datetime, CORE_COMPONENT)); - pErrObj = "msg"; - CHKiRet(msgClassInit(NULL)); - pErrObj = "str,"; - CHKiRet(strmClassInit(NULL)); - pErrObj = "wti"; - CHKiRet(wtiClassInit(NULL)); - pErrObj = "wtp"; - CHKiRet(wtpClassInit(NULL)); - pErrObj = "queue"; - CHKiRet(queueClassInit(NULL)); - pErrObj = "vmstk"; - CHKiRet(vmstkClassInit(NULL)); - pErrObj = "sysvar"; - CHKiRet(sysvarClassInit(NULL)); pErrObj = "vm"; - CHKiRet(vmClassInit(NULL)); CHKiRet(objUse(vm, CORE_COMPONENT)); - pErrObj = "vmop"; - CHKiRet(vmopClassInit(NULL)); - pErrObj = "vmprg"; - CHKiRet(vmprgClassInit(NULL)); - pErrObj = "ctok_token"; - CHKiRet(ctok_tokenClassInit(NULL)); - pErrObj = "ctok"; - CHKiRet(ctokClassInit(NULL)); pErrObj = "expr"; - CHKiRet(exprClassInit(NULL)); CHKiRet(objUse(expr, CORE_COMPONENT)); pErrObj = "conf"; - CHKiRet(confClassInit(NULL)); CHKiRet(objUse(conf, CORE_COMPONENT)); - /* dummy "classes" */ + /* intialize some dummy classes that are not part of the runtime */ pErrObj = "action"; CHKiRet(actionClassInit()); pErrObj = "template"; CHKiRet(templateInit()); - pErrObj = "str"; - CHKiRet(strInit()); /* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */ pErrObj = "net"; @@ -2942,7 +2870,6 @@ GlobalClassExit(void) objRelease(datetime, CORE_COMPONENT); /* TODO: implement the rest of the deinit */ - confClassExit(); #if 0 CHKiRet(datetimeClassInit(NULL)); CHKiRet(msgClassInit(NULL)); @@ -2972,7 +2899,7 @@ GlobalClassExit(void) CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(module, CORE_COMPONENT)); #endif - objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ + rsrtExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ RETiRet; } @@ -3053,18 +2980,19 @@ int realMain(int argc, char **argv) DEFiRet; register int i; - register char *p; + register uchar *p; int num_fds; int ch; struct hostent *hent; extern int optind; extern char *optarg; struct sigaction sigAct; - int bIsFirstOption = 1; int bEOptionWasGiven = 0; int bImUxSockLoaded = 0; /* already generated a $ModLoad imuxsock? */ char *arg; /* for command line option processing */ uchar legacyConfLine[80]; + uchar *LocalHostName; + uchar *LocalDomain; /* first, parse the command line options. We do not carry out any actual work, just * see what we should do. This relieves us from certain anomalies and we can process @@ -3103,11 +3031,6 @@ int realMain(int argc, char **argv) CHKiRet(bufOptAdd(ch, optarg)); break; case 'c': /* compatibility mode */ - if(!bIsFirstOption) { - fprintf(stderr, "-c option MUST be specified as the first option - aborting...\n"); - usage(); - exit(1); - } iCompatibilityMode = atoi(optarg); break; case 'd': /* debug - must be handled now, so that debug is active during init! */ @@ -3148,7 +3071,6 @@ int realMain(int argc, char **argv) default: usage(); } - bIsFirstOption = 0; /* we already saw an option character */ } if ((argc -= optind)) @@ -3178,11 +3100,11 @@ int realMain(int argc, char **argv) * error log messages, which need the correct hostname. -- rgerhards, 2008-04-04 */ net.getLocalHostname(&LocalHostName); - if((p = strchr((char*)LocalHostName, '.'))) { + if((p = (uchar*)strchr((char*)LocalHostName, '.'))) { *p++ = '\0'; LocalDomain = p; } else { - LocalDomain = ""; + LocalDomain = (uchar*)""; /* It's not clearly defined whether gethostname() * should return the simple hostname or the fqdn. A @@ -3202,7 +3124,7 @@ int realMain(int argc, char **argv) free(LocalHostName); CHKmalloc(LocalHostName = (uchar*)strdup(hent->h_name)); - if((p = strchr((char*)LocalHostName, '.'))) + if((p = (uchar*)strchr((char*)LocalHostName, '.'))) { *p++ = '\0'; LocalDomain = p; @@ -3211,8 +3133,15 @@ int realMain(int argc, char **argv) } /* Convert to lower case to recognize the correct domain laterly */ - for (p = (char *)LocalDomain ; *p ; p++) + for(p = LocalDomain ; *p ; p++) *p = (char)tolower((int)*p); + + /* we now have our hostname and can set it inside the global vars. + * TODO: think if all of this would better be a runtime function + * rgerhards, 2008-04-17 + */ + glbl.SetLocalHostName(LocalHostName); + glbl.SetLocalDomain(LocalDomain); /* initialize the objects */ if((iRet = modInitIminternal()) != RS_RET_OK) { @@ -3233,10 +3162,10 @@ int realMain(int argc, char **argv) dbgprintf("deque option %c, optarg '%s'\n", ch, arg); switch((char)ch) { case '4': - family = PF_INET; + glbl.SetDefPFFamily(PF_INET); break; case '6': - family = PF_INET6; + glbl.SetDefPFFamily(PF_INET6); break; case 'A': send_to_all++; @@ -3264,7 +3193,7 @@ int realMain(int argc, char **argv) break; case 'h': if(iCompatibilityMode < 3) { - errmsg.LogError(NO_ERRCODE, "WARNING: -h option is no longer supported - ignored"); + errmsg.LogError(0, NO_ERRCODE, "WARNING: -h option is no longer supported - ignored"); } else { usage(); /* for v3 and above, it simply is an error */ } @@ -3273,10 +3202,10 @@ int realMain(int argc, char **argv) PidFile = arg; break; case 'l': - if (LocalHosts) { + if(glbl.GetLocalHosts() != NULL) { fprintf (stderr, "rsyslogd: Only one -l argument allowed, the first one is taken.\n"); } else { - LocalHosts = crunch_list(arg); + glbl.SetLocalHosts(crunch_list(arg)); } break; case 'm': /* mark interval */ @@ -3326,10 +3255,10 @@ int realMain(int argc, char **argv) fprintf(stderr, "-r option only supported in compatibility modes 0 to 2 - ignored\n"); break; case 's': - if (StripDomains) { + if(glbl.GetStripDomains() != NULL) { fprintf (stderr, "rsyslogd: Only one -s argument allowed, the first one is taken.\n"); } else { - StripDomains = crunch_list(arg); + glbl.SetStripDomains(crunch_list(arg)); } break; case 't': /* enable tcp logging */ @@ -3342,11 +3271,11 @@ int realMain(int argc, char **argv) if(atoi(arg) == 1) bParseHOSTNAMEandTAG = 0; break; - case 'w': /* disable disallowed host warnings */ - option_DisallowWarning = 0; + case 'w': /* disable disallowed host warnigs */ + glbl.SetOption_DisallowWarning(0); break; case 'x': /* disable dns for remote messages */ - DisableDNS = 1; + glbl.SetDisableDNS(1); break; case '?': default: @@ -3359,7 +3288,7 @@ int realMain(int argc, char **argv) /* process compatibility mode settings */ if(iCompatibilityMode < 3) { - errmsg.LogError(NO_ERRCODE, "WARNING: rsyslogd is running in compatibility mode. Automatically " + errmsg.LogError(0, NO_ERRCODE, "WARNING: rsyslogd is running in compatibility mode. Automatically " "generated config directives may interfer with your rsyslog.conf settings. " "We suggest upgrading your config and adding -c3 as the first " "rsyslogd option."); @@ -3374,7 +3303,7 @@ int realMain(int argc, char **argv) } if(bEOptionWasGiven && iCompatibilityMode < 3) { - errmsg.LogError(NO_ERRCODE, "WARNING: \"message repeated n times\" feature MUST be turned on in " + errmsg.LogError(0, NO_ERRCODE, "WARNING: \"message repeated n times\" feature MUST be turned on in " "rsyslog.conf - CURRENTLY EVERY MESSAGE WILL BE LOGGED. Visit " "http://www.rsyslog.com/rptdmsgreduction to learn " "more and cast your vote if you want us to keep this feature."); diff --git a/syslogd.h b/tools/syslogd.h index 46de8d28..e866a16b 100644 --- a/syslogd.h +++ b/tools/syslogd.h @@ -29,44 +29,11 @@ #include "linkedlist.h" #include "expr.h" -/* portability: not all platforms have these defines, so we - * define them here if they are missing. -- rgerhards, 2008-03-04 - */ -#ifndef LOG_MAKEPRI -# define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) -#endif -#ifndef LOG_PRI -# define LOG_PRI(p) ((p) & LOG_PRIMASK) -#endif -#ifndef LOG_FAC -# define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) -#endif - -#ifdef USE_NETZIP -/* config param: minimum message size to try compression. The smaller - * the message, the less likely is any compression gain. We check for - * gain before we submit the message. But to do so we still need to - * do the (costly) compress() call. The following setting sets a size - * for which no call to compress() is done at all. This may result in - * a few more bytes being transmited but better overall performance. - * Note: I have not yet checked the minimum UDP packet size. It might be - * that we do not save anything by compressing very small messages, because - * UDP might need to pad ;) - * rgerhards, 2006-11-30 - */ -#define MIN_SIZE_FOR_COMPRESS 60 +#ifndef _PATH_CONSOLE +#define _PATH_CONSOLE "/dev/console" #endif -#define MAXLINE 2048 /* maximum line length */ - -/* Flags to logmsg(). - */ -#define NOFLAG 0x000 /* no flag is set (to be used when a flag must be specified and none is required) */ -#define INTERNAL_MSG 0x001 /* msg generated by logmsgInternal() --> special handling */ -#define SYNC_FILE 0x002 /* do fsync on file after printing */ -#define ADDDATE 0x004 /* add a date to the message */ -#define MARK 0x008 /* this message is a mark */ /* This structure represents the files that will have log * copies printed. @@ -113,54 +80,20 @@ struct filed { linkedList_t llActList; /* list of configured actions */ }; -typedef struct filed selector_t; /* new type name */ -#define MSG_PARSE_HOSTNAME 1 -#define MSG_DONT_PARSE_HOSTNAME 0 -rsRetVal parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType); #include "net.h" /* TODO: remove when you remoe isAllowedSender from here! */ void untty(void); rsRetVal selectorConstruct(selector_t **ppThis); -rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName); -rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl); -int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep); rsRetVal selectorDestruct(void *pVal); rsRetVal selectorAddList(selector_t *f); /* the following prototypes should go away once we have an input * module interface -- rgerhards, 2007-12-12 */ -rsRetVal logmsgInternal(int pri, char *msg, int flags); void logmsg(msg_t *pMsg, int flags); -rsRetVal submitMsg(msg_t *pMsg); -extern int glblHadMemShortage; /* indicates if we had memory shortage some time during the run */ -extern uchar *LocalHostName; -extern int family; extern int NoHops; extern int send_to_all; -extern int option_DisallowWarning; extern int Debug; -extern char**LocalHosts; -extern int DisableDNS; -extern char **StripDomains; -extern char *LocalDomain; -extern int bDropMalPTRMsgs; -extern char ctty[]; -extern int MarkInterval; -extern int bReduceRepeatMsgs; -extern int bActExecWhenPrevSusp; -extern int iActExecOnceInterval; - -/* Intervals at which we flush out "message repeated" messages, - * in seconds after previous message is logged. After each flush, - * we move to the next interval until we reach the largest. - * TODO: move this to action object! - */ -extern int repeatinterval[2]; -#define MAXREPEAT ((int)((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)) -#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) -#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \ - (f)->f_repeatcount = MAXREPEAT; \ - } +#include "dirty.h" #endif /* #ifndef SYSLOGD_H_INCLUDED */ |