Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support many RFC3164 old formats (OpenBSD, IQTS-IP200, Synology NAS, Unifi UAP and UGP) #13

Merged
merged 11 commits into from
May 20, 2024
327 changes: 169 additions & 158 deletions docs/rfc3164.dot

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion nontransparent/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ func Example_withoutTrailerAtEnd() {
// Output:
// ([]syslog.Result) (len=1) {
// (syslog.Result) {
// Message: (syslog.Message) <nil>,
// Error: (*ragel.ReadingError)(unexpected EOF)
// }
// }
}

func Example_bestEffortWithoutTrailerAtEnd() {
results := []syslog.Result{}
acc := func(res *syslog.Result) {
results = append(results, *res)
}
// Notice the message ends without trailer but we catch it anyway
r := strings.NewReader("<1>1 2003-10-11T22:14:15.003Z host.local - - - - mex")
NewParser(syslog.WithBestEffort(), syslog.WithListener(acc)).Parse(r)
output(results)
// Output:
// ([]syslog.Result) (len=1) {
// (syslog.Result) {
// Message: (*rfc5424.SyslogMessage)({
// Base: (syslog.Base) {
// Facility: (*uint8)(0),
Expand All @@ -37,7 +55,7 @@ func Example_withoutTrailerAtEnd() {
// Version: (uint16) 1,
// StructuredData: (*map[string]map[string]string)(<nil>)
// }),
// Error: (*ragel.ReadingError)(unexpected EOF)
// Error: (error) <nil>
// }
// }
}
Expand Down
27 changes: 26 additions & 1 deletion nontransparent/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io"

syslog "github.com/leodido/go-syslog/v4"
"github.com/leodido/go-syslog/v4/rfc3164"
"github.com/leodido/go-syslog/v4/rfc5424"
parser "github.com/leodido/ragel-machinery/parser"
)
Expand Down Expand Up @@ -182,7 +183,8 @@ func (m *machine) OnCompletion() {
// Try to parse last chunk as a candidate
if m.readError != nil && len(m.lastChunk) > 0 {
res, err := m.internal.Parse(m.lastChunk)
if err == nil {
if err == nil && !m.bestEffort {
res = nil
err = m.readError
}
m.emit(&syslog.Result{
Expand Down Expand Up @@ -216,6 +218,29 @@ func NewParser(options ...syslog.ParserOption) syslog.Parser {
return m
}

func NewParserRFC3164(options ...syslog.ParserOption) syslog.Parser {
m := &machine{
emit: func(*syslog.Result) { /* noop */ },
}

for _, opt := range options {
m = opt(m).(*machine)
}

// No error can happens since during its setting we check the trailer type passed in
trailer, _ := m.trailertyp.Value()
m.trailer = byte(trailer)

// Create internal parser depending on options
if m.bestEffort {
m.internal = rfc3164.NewMachine(rfc3164.WithBestEffort())
} else {
m.internal = rfc3164.NewMachine()
}

return m
}

// WithMaxMessageLength does nothing for this parser.
func (m *machine) WithMaxMessageLength(length int) {}

Expand Down
27 changes: 26 additions & 1 deletion nontransparent/parser.go.rl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
parser "github.com/leodido/ragel-machinery/parser"
syslog "github.com/leodido/go-syslog/v4"
"github.com/leodido/go-syslog/v4/rfc5424"
"github.com/leodido/go-syslog/v4/rfc3164"
)

%%{
Expand Down Expand Up @@ -78,7 +79,8 @@ func (m *machine) OnCompletion() {
// Try to parse last chunk as a candidate
if m.readError != nil && len(m.lastChunk) > 0 {
res, err := m.internal.Parse(m.lastChunk)
if err == nil {
if err == nil && !m.bestEffort {
res = nil
err = m.readError
}
m.emit(&syslog.Result{
Expand Down Expand Up @@ -112,6 +114,29 @@ func NewParser(options ...syslog.ParserOption) syslog.Parser {
return m
}

func NewParserRFC3164(options ...syslog.ParserOption) syslog.Parser {
m := &machine{
emit: func(*syslog.Result) { /* noop */ },
}

for _, opt := range options {
m = opt(m).(*machine)
}

// No error can happens since during its setting we check the trailer type passed in
trailer, _ := m.trailertyp.Value()
m.trailer = byte(trailer)

// Create internal parser depending on options
if m.bestEffort {
m.internal = rfc3164.NewMachine(rfc3164.WithBestEffort())
} else {
m.internal = rfc3164.NewMachine()
}

return m
}

// WithMaxMessageLength does nothing for this parser.
func (m *machine) WithMaxMessageLength(length int) {}

Expand Down
8 changes: 2 additions & 6 deletions nontransparent/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,12 @@ func getTestCases() []testCase {
false,
[]syslog.Result{
{
Message: (&rfc5424.SyslogMessage{}).SetPriority(3).SetVersion(1),
Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()),
Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()),
},
},
[]syslog.Result{
{
Message: (&rfc5424.SyslogMessage{}).SetPriority(3).SetVersion(1),
Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()),
},
},
},
Expand Down Expand Up @@ -98,8 +96,7 @@ func getTestCases() []testCase {
Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1),
},
{
Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1),
Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()),
Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()),
},
},
[]syslog.Result{
Expand All @@ -108,7 +105,6 @@ func getTestCases() []testCase {
},
{
Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1),
Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()),
},
},
},
Expand Down
86 changes: 86 additions & 0 deletions octetcounting/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,89 @@ func Example_channel() {
// Error: (*errors.errorString)(parsing error [col 4])
// }
}

func Example_channelOpenBSD() {
messages := []string{
"42 <46>Apr 28 11:53:44 syslogd[18823]: start\n",
"44 <47>Apr 28 11:53:44 syslogd[18823]: running\n",
"88 <86>Apr 28 11:53:46 doas: catap ran command ls / as root from /home/catap/src/go-syslog\n",
}

r, w := io.Pipe()

go func() {
defer w.Close()

for _, m := range messages {
w.Write([]byte(m))
time.Sleep(time.Millisecond * 220)
}
}()

c := make(chan syslog.Result)
emit := func(res *syslog.Result) {
c <- *res
}

parser := NewParserRFC3164(syslog.WithBestEffort(), syslog.WithListener(emit))
go func() {
defer close(c)
parser.Parse(r)
}()

for r := range c {
output(r)
}

r.Close()

// Output:
// (syslog.Result) {
// Message: (*rfc3164.SyslogMessage)({
// Base: (syslog.Base) {
// Facility: (*uint8)(5),
// Severity: (*uint8)(6),
// Priority: (*uint8)(46),
// Timestamp: (*time.Time)(0000-04-28 11:53:44 +0000 UTC),
// Hostname: (*string)(<nil>),
// Appname: (*string)((len=7) "syslogd"),
// ProcID: (*string)((len=5) "18823"),
// MsgID: (*string)(<nil>),
// Message: (*string)((len=5) "start")
// }
// }),
// Error: (error) <nil>
// }
// (syslog.Result) {
// Message: (*rfc3164.SyslogMessage)({
// Base: (syslog.Base) {
// Facility: (*uint8)(5),
// Severity: (*uint8)(7),
// Priority: (*uint8)(47),
// Timestamp: (*time.Time)(0000-04-28 11:53:44 +0000 UTC),
// Hostname: (*string)(<nil>),
// Appname: (*string)((len=7) "syslogd"),
// ProcID: (*string)((len=5) "18823"),
// MsgID: (*string)(<nil>),
// Message: (*string)((len=7) "running")
// }
// }),
// Error: (error) <nil>
// }
// (syslog.Result) {
// Message: (*rfc3164.SyslogMessage)({
// Base: (syslog.Base) {
// Facility: (*uint8)(10),
// Severity: (*uint8)(6),
// Priority: (*uint8)(86),
// Timestamp: (*time.Time)(0000-04-28 11:53:46 +0000 UTC),
// Hostname: (*string)(<nil>),
// Appname: (*string)((len=4) "doas"),
// ProcID: (*string)(<nil>),
// MsgID: (*string)(<nil>),
// Message: (*string)((len=61) "catap ran command ls / as root from /home/catap/src/go-syslog")
// }
// }),
// Error: (error) <nil>
// }
}
24 changes: 23 additions & 1 deletion octetcounting/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

syslog "github.com/leodido/go-syslog/v4"
"github.com/leodido/go-syslog/v4/rfc5424"
"github.com/leodido/go-syslog/v4/rfc3164"
)

// parser is capable to parse the input stream containing syslog messages with octetcounting framing.
Expand Down Expand Up @@ -43,6 +44,26 @@ func NewParser(opts ...syslog.ParserOption) syslog.Parser {
return p
}

func NewParserRFC3164(opts ...syslog.ParserOption) syslog.Parser {
p := &parser{
emit: func(*syslog.Result) { /* noop */ },
maxMessageLength: 1024,
}

for _, opt := range opts {
p = opt(p).(*parser)
}

// Create internal parser depending on options
if p.bestEffort {
p.internal = rfc3164.NewMachine(rfc3164.WithBestEffort())
} else {
p.internal = rfc3164.NewMachine()
}

return p
}

func (p *parser) WithMaxMessageLength(length int) {
p.maxMessageLength = length
}
Expand Down Expand Up @@ -134,7 +155,8 @@ func (p *parser) run() {
// Next we MUST see an EOF otherwise the parsing we'll start again
if tok = p.scan(); tok.typ == EOF {
break
} else {
} else if tok.typ != LF {
// but some syslog may separate lines with octet by \n, ignore it
p.unscan()
}
}
Expand Down
4 changes: 2 additions & 2 deletions octetcounting/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ func getTestCases() []testCase {
},
},
{
descr: "1st ok/2nd ok/3rd ok",
input: "48 <1>1 2003-10-11T22:14:15.003Z host.local - - - -25 <3>1 - host.local - - - -38 <2>1 - host.local su - - - κόσμε",
descr: "1st ok/2nd ok/LF/3rd ok", // LF means new line aka \n
input: "48 <1>1 2003-10-11T22:14:15.003Z host.local - - - -25 <3>1 - host.local - - - -\n38 <2>1 - host.local su - - - κόσμε",
// results w/o best effort
results: []syslog.Result{
{
Expand Down
9 changes: 9 additions & 0 deletions octetcounting/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
// eof represents a marker byte for the end of the reader
var eof = byte(0)

// lf represents the NewLine
var lf = byte(10)

// ws represents the whitespace
var ws = byte(32)

Expand Down Expand Up @@ -73,6 +76,12 @@ func (s *Scanner) Scan() (tok Token) {
return Token{
typ: EOF,
}
case lf:
s.ready = true
return Token{
typ: LF,
lit: []byte{lf},
}
case ws:
s.ready = true
return Token{
Expand Down
5 changes: 3 additions & 2 deletions octetcounting/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type TokenType int
const (
ILLEGAL TokenType = iota
EOF
LF
WS
MSGLEN
SYSLOGMSG
Expand All @@ -32,9 +33,9 @@ func (t Token) String() string {
}
}

const tokentypename = "ILLEGALEOFWSMSGLENSYSLOGMSG"
const tokentypename = "ILLEGALEOFLFWSMSGLENSYSLOGMSG"

var tokentypeindex = [...]uint8{0, 7, 10, 12, 18, 27}
var tokentypeindex = [...]uint8{0, 7, 10, 12, 14, 20, 29}

// String outputs the string representation of the receiving TokenType.
func (i TokenType) String() string {
Expand Down
2 changes: 1 addition & 1 deletion rfc3164/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func Example_besteffort() {
// Appname: (*string)(<nil>),
// ProcID: (*string)(<nil>),
// MsgID: (*string)(<nil>),
// Message: (*string)(<nil>)
// Message: (*string)((len=1) "-")
// }
// })
}
Expand Down
Loading