Skip to content

Commit a08d7be

Browse files
committed
pmrfc3164: fix HOSTNAME parse for IPv6 address literals
Non-tech: IPv6 support in rfc3164 parsing so legacy pipelines keep headers intact when devices send IPv6 addresses instead of DNS names. Impact: user-visible bugfix; RFC3164 forwarding stays well-formed for IPv6-only sources. Before: an IPv6 literal in HOSTNAME broke parsing and misaligned the forwarded header. After: valid IPv6 literals (optionally bracketed) are accepted as HOSTNAME; TAG parsing remains in sync. Technical: when normal hostname recognition fails, the parser scans the next token up to space. If it contains a colon and validates via inet_pton(AF_INET6), the token becomes HOSTNAME. Bracketed tokens are accepted only when permit.squarebracketsinhostname is on. The parse pointer advances past the token and any trailing space to preserve the RFC3164 flow. Hostname size limits (CONF_HOSTNAME_MAXSIZE) remain. A regression test (pmrfc3164-ipv6-hostname.sh) exercises both forms and is added to tests/Makefile.am. Fixes: rsyslog#3012 With the help of AI agents: Google Jules
1 parent f4caae0 commit a08d7be

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

tests/Makefile.am

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ TESTS_DEFAULT = \
9595
prop-programname-with-slashes.sh \
9696
hostname-with-slash-pmrfc5424.sh \
9797
hostname-with-slash-pmrfc3164.sh \
98+
pmrfc3164-ipv6-hostname.sh \
9899
hostname-with-slash-dflt-invld.sh \
99100
func-substring-invld-startpos.sh \
100101
func-substring-large-endpos.sh \
@@ -2733,34 +2734,60 @@ test_files = testbench.h runtime-dummy.c
27332734
DISTCLEANFILES=rsyslog.pid
27342735

27352736
distclean-local:
2737+
27362738
rm -rf .dep_cache .dep_wrk
27372739

27382740

27392741

27402742
EXTRA_DIST += \
2743+
27412744
faketime_common.sh \
2745+
27422746
privdrop_common.sh \
2747+
27432748
rscript_compare-common.sh \
2749+
27442750
rscript_parse_time_get-ts.py \
2751+
27452752
queue-persist-drvr.sh \
2753+
27462754
daqueue-persist-drvr.sh \
2755+
27472756
snmptrapreceiverv2.py \
2757+
27482758
validate_json_shell.sh \
2759+
27492760
tls-check-for-certificate.sh \
2761+
27502762
set-envvars.in \
2763+
27512764
urlencode.py \
2765+
27522766
dns_srv_mock.py \
2767+
27532768
omrelp-keepalive.sh \
2769+
27542770
improg-simul.sh \
2771+
27552772
sndrcv_drvr.sh \
2773+
27562774
sndrcv_drvr_noexit.sh \
2775+
27572776
diag.sh \
2777+
27582778
testsuites/mmexternal-SegFault-mm-python.py \
2779+
27592780
testsuites/mmsnareparse/sample-custom-pattern.data \
2781+
27602782
testsuites/mmsnareparse/sample-windows2022-security.data \
2783+
27612784
testsuites/mmsnareparse/sample-windows2025-security.data \
2785+
27622786
testsuites/mmsnareparse/sample-events.data \
2787+
27632788
1.rstest 2.rstest 3.rstest err1.rstest \
2789+
2790+
27642791
testsuites/include-std1-omfile-action.conf \
27652792
testsuites/include-std2-omfile-action.conf \
27662793
tls-certs/ca-key.pem \

tests/pmrfc3164-ipv6-hostname.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
## Test RFC3164 parser accepts IPv6 literals as HOSTNAME.
3+
. ${srcdir:=.}/diag.sh init
4+
generate_conf
5+
add_conf '
6+
module(load="../plugins/imtcp/.libs/imtcp")
7+
input(type="imtcp" port="0" listenPortFileName="'$RSYSLOG_DYNNAME'.tcpflood_port" ruleset="customparser")
8+
parser(name="custom.rfc3164" type="pmrfc3164" permit.squarebracketsinhostname="on")
9+
template(name="outfmt" type="string" string="%hostname%\n")
10+
11+
ruleset(name="customparser" parser="custom.rfc3164") {
12+
action(type="omfile" template="outfmt" file="'$RSYSLOG_OUT_LOG'")
13+
}
14+
'
15+
startup
16+
tcpflood -m1 -M "\"<129>Mar 10 01:00:00 2001:db8::1 tag: msgnum:1\""
17+
tcpflood -m1 -M "\"<129>Mar 10 01:00:00 [2001:db8::2] tag: msgnum:2\""
18+
shutdown_when_empty
19+
wait_shutdown
20+
export EXPECTED='2001:db8::1
21+
[2001:db8::2]'
22+
cmp_exact
23+
24+
exit_test

tools/pmrfc3164.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <errno.h>
3333
#include <ctype.h>
3434
#include <unistd.h>
35+
#include <arpa/inet.h>
3536
#include "syslogd.h"
3637
#include "conf.h"
3738
#include "syslogd-types.h"
@@ -63,6 +64,32 @@ DEFobjCurrIf(ruleset);
6364
/* static data */
6465
static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
6566

67+
static sbool isIPv6HostnameToken(const uchar* token, const size_t token_len, const int permitSquareBrackets) {
68+
struct in6_addr addr;
69+
char addr_buf[INET6_ADDRSTRLEN];
70+
const uchar* addr_start = token;
71+
size_t addr_len = token_len;
72+
73+
if (token_len == 0 || token_len >= (sizeof(addr_buf) - 1)) {
74+
return RSFALSE;
75+
}
76+
77+
if (token[0] == '[' && token[token_len - 1] == ']') {
78+
if (!permitSquareBrackets) {
79+
return RSFALSE;
80+
}
81+
addr_start = token + 1;
82+
addr_len = token_len - 2;
83+
if (addr_len == 0 || addr_len >= (sizeof(addr_buf) - 1)) {
84+
return RSFALSE;
85+
}
86+
}
87+
88+
memcpy(addr_buf, addr_start, addr_len);
89+
addr_buf[addr_len] = '\0';
90+
return (inet_pton(AF_INET6, addr_buf, &addr) == 1) ? RSTRUE : RSFALSE;
91+
}
92+
6693

6794
/* parser instance parameters */
6895
static struct cnfparamdescr parserpdescr[] = {
@@ -430,6 +457,7 @@ BEGINparse2
430457
MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
431458
} else {
432459
int isHostName = 0;
460+
size_t ipv6HostLen = 0;
433461
if (i > 0) {
434462
if (bHadSBracket) {
435463
if (p2parse[i] == ']') {
@@ -445,6 +473,26 @@ BEGINparse2
445473
if (p2parse[i] != ' ') isHostName = 0;
446474
}
447475

476+
if (!isHostName && lenMsg > 0) {
477+
for (ipv6HostLen = 0; ipv6HostLen < (size_t)lenMsg && p2parse[ipv6HostLen] != ' ' &&
478+
ipv6HostLen < (CONF_HOSTNAME_MAXSIZE - 1);
479+
++ipv6HostLen) {
480+
}
481+
if (ipv6HostLen > 0 && memchr(p2parse, ':', ipv6HostLen) != NULL &&
482+
isIPv6HostnameToken(p2parse, ipv6HostLen, pInst->bPermitSquareBracketsInHostname)) {
483+
memcpy(bufParseHOSTNAME, p2parse, ipv6HostLen);
484+
bufParseHOSTNAME[ipv6HostLen] = '\0';
485+
MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, ipv6HostLen);
486+
if (ipv6HostLen < (size_t)lenMsg && p2parse[ipv6HostLen] == ' ') {
487+
p2parse += ipv6HostLen + 1;
488+
lenMsg -= (int)(ipv6HostLen + 1);
489+
} else {
490+
p2parse += ipv6HostLen;
491+
lenMsg -= (int)ipv6HostLen;
492+
}
493+
}
494+
}
495+
448496
if (isHostName) {
449497
/* we got a hostname! */
450498
p2parse += i + 1; /* "eat" it (including SP delimiter) */

0 commit comments

Comments
 (0)