diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/changelog golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/changelog --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/changelog 2016-03-18 09:49:09.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/changelog 2017-07-07 17:48:16.000000000 +0000 @@ -1,3 +1,19 @@ +golang-gopkg-mcuadros-go-syslog.v2 (2.2.1-1) unstable; urgency=medium + + [ Paul Tagliamonte ] + * Team upload. + * Use a secure transport for the Vcs-Git and Vcs-Browser URL + + [ Dr. Tobias Quathamer ] + * New upstream version 2.2.1 + * Use debhelper v10 + * Update to Standards-Version 4.0.0 + - Use HTTPS URL for d/copyright + * Use golang-any + * Update expected error string to fix FTBFS. (Closes: #866662) + + -- Dr. Tobias Quathamer Fri, 07 Jul 2017 19:48:16 +0200 + golang-gopkg-mcuadros-go-syslog.v2 (2.1.1-1) unstable; urgency=medium * Initial release (Closes: #818581). diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/compat golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/compat --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/compat 2016-03-18 09:06:26.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/compat 2017-07-07 17:38:51.000000000 +0000 @@ -1 +1 @@ -9 +10 diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/control golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/control --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/control 2016-03-18 22:45:46.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/control 2017-07-07 17:43:11.000000000 +0000 @@ -3,22 +3,21 @@ Priority: extra Maintainer: Debian Go Packaging Team Uploaders: Dmitry Smirnov -Build-Depends: debhelper (>= 9), +Build-Depends: debhelper (>= 10), dh-golang, - golang-go, + golang-any, golang-github-jeromer-syslogparser-dev, golang-check.v1-dev | golang-gopkg-check.v1-dev -Standards-Version: 3.9.7 +Standards-Version: 4.0.0 Homepage: https://github.com/mcuadros/go-syslog Vcs-Browser: https://anonscm.debian.org/cgit/pkg-go/packages/golang-gopkg-mcuadros-go-syslog.v2.git -Vcs-Git: git://anonscm.debian.org/pkg-go/packages/golang-gopkg-mcuadros-go-syslog.v2.git +Vcs-Git: https://anonscm.debian.org/git/pkg-go/packages/golang-gopkg-mcuadros-go-syslog.v2.git XS-Go-Import-Path: gopkg.in/mcuadros/go-syslog.v2 Package: golang-gopkg-mcuadros-go-syslog.v2-dev Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, - golang-go, golang-github-jeromer-syslogparser-dev, golang-check.v1-dev | golang-gopkg-check.v1-dev Description: syslog server library for Golang diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/copyright golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/copyright --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/copyright 2016-03-18 09:06:26.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/copyright 2017-07-07 17:38:53.000000000 +0000 @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: go-syslog.v2 Source: https://github.com/mcuadros/go-syslog diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/patches/0001-Update-expected-error-string-to-fix-FTBFS.patch golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/patches/0001-Update-expected-error-string-to-fix-FTBFS.patch --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/patches/0001-Update-expected-error-string-to-fix-FTBFS.patch 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/patches/0001-Update-expected-error-string-to-fix-FTBFS.patch 2017-07-07 17:47:30.000000000 +0000 @@ -0,0 +1,20 @@ +From: "Dr. Tobias Quathamer" +Date: Fri, 7 Jul 2017 19:46:47 +0200 +Subject: Update expected error string to fix FTBFS. + +Closes: #866662 +--- + format/rfc6587_test.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/format/rfc6587_test.go b/format/rfc6587_test.go +index 24686ad..ac16071 100644 +--- a/format/rfc6587_test.go ++++ b/format/rfc6587_test.go +@@ -85,5 +85,5 @@ func (s *FormatSuite) TestRFC6587_GetSplitBadSplit(c *C) { + c.Assert(r, NotNil) + + err := scanner.Err() +- c.Assert(err, ErrorMatches, "strconv.ParseInt: parsing \".2\": invalid syntax") ++ c.Assert(err, ErrorMatches, "strconv.Atoi: parsing \".2\": invalid syntax") + } diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/patches/series golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/patches/series --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/debian/patches/series 2017-07-07 17:47:30.000000000 +0000 @@ -0,0 +1 @@ +0001-Update-expected-error-string-to-fix-FTBFS.patch diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/doc.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/doc.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/doc.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/doc.go 2017-07-07 17:38:23.000000000 +0000 @@ -1,5 +1,5 @@ /* Syslog server library for go, build easy your custom syslog server -over UDP, TCP or Unix sockets using RFC3164 or RFC5424 +over UDP, TCP or Unix sockets using RFC3164, RFC5424 and RFC6587 */ -package syslog +package syslog // import "gopkg.in/mcuadros/go-syslog.v2" diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/automatic.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/automatic.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/automatic.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/automatic.go 2017-07-07 17:38:23.000000000 +0000 @@ -6,9 +6,8 @@ "errors" "strconv" - "github.com/jeromer/syslogparser" - "github.com/jeromer/syslogparser/rfc3164" - "github.com/jeromer/syslogparser/rfc5424" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424" ) /* Selecting an 'Automatic' format detects incoming format (i.e. RFC3164 vs RFC5424) and Framing @@ -57,12 +56,12 @@ return detectedUnknown, nil } -func (f *Automatic) GetParser(line []byte) syslogparser.LogParser { +func (f *Automatic) GetParser(line []byte) LogParser { switch format, _ := detect(line); format { case detectedRFC3164: - return rfc3164.NewParser(line) + return &parserWrapper{rfc3164.NewParser(line)} case detectedRFC5424: - return rfc5424.NewParser(line) + return &parserWrapper{rfc5424.NewParser(line)} default: // If the line was an RFC6587 line, the splitter should already have removed the length, // so one of the above two will be chosen if the line is correctly formed. However, it @@ -70,7 +69,7 @@ // will return detectedRFC6587. The line may also simply be malformed after the length in // which case we will have detectedUnknown. In this case we return the simplest parser so // the illegally formatted line is properly handled - return rfc3164.NewParser(line) + return &parserWrapper{rfc3164.NewParser(line)} } } diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/format.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/format.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/format.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/format.go 2017-07-07 17:38:23.000000000 +0000 @@ -2,11 +2,28 @@ import ( "bufio" + "time" - "github.com/jeromer/syslogparser" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser" ) +type LogParts map[string]interface{} + +type LogParser interface { + Parse() error + Dump() LogParts + Location(*time.Location) +} + type Format interface { - GetParser([]byte) syslogparser.LogParser + GetParser([]byte) LogParser GetSplitFunc() bufio.SplitFunc } + +type parserWrapper struct { + syslogparser.LogParser +} + +func (w *parserWrapper) Dump() LogParts { + return LogParts(w.LogParser.Dump()) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc3164.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc3164.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc3164.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc3164.go 2017-07-07 17:38:23.000000000 +0000 @@ -3,14 +3,13 @@ import ( "bufio" - "github.com/jeromer/syslogparser" - "github.com/jeromer/syslogparser/rfc3164" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164" ) type RFC3164 struct{} -func (f *RFC3164) GetParser(line []byte) syslogparser.LogParser { - return rfc3164.NewParser(line) +func (f *RFC3164) GetParser(line []byte) LogParser { + return &parserWrapper{rfc3164.NewParser(line)} } func (f *RFC3164) GetSplitFunc() bufio.SplitFunc { diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc5424.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc5424.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc5424.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc5424.go 2017-07-07 17:38:23.000000000 +0000 @@ -3,14 +3,13 @@ import ( "bufio" - "github.com/jeromer/syslogparser" - "github.com/jeromer/syslogparser/rfc5424" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424" ) type RFC5424 struct{} -func (f *RFC5424) GetParser(line []byte) syslogparser.LogParser { - return rfc5424.NewParser(line) +func (f *RFC5424) GetParser(line []byte) LogParser { + return &parserWrapper{rfc5424.NewParser(line)} } func (f *RFC5424) GetSplitFunc() bufio.SplitFunc { diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc6587.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc6587.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc6587.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc6587.go 2017-07-07 17:38:23.000000000 +0000 @@ -5,14 +5,13 @@ "bytes" "strconv" - "github.com/jeromer/syslogparser" - "github.com/jeromer/syslogparser/rfc5424" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424" ) type RFC6587 struct{} -func (f *RFC6587) GetParser(line []byte) syslogparser.LogParser { - return rfc5424.NewParser(line) +func (f *RFC6587) GetParser(line []byte) LogParser { + return &parserWrapper{rfc5424.NewParser(line)} } func (f *RFC6587) GetSplitFunc() bufio.SplitFunc { @@ -28,11 +27,15 @@ pLength := data[0:i] length, err := strconv.Atoi(string(pLength)) if err != nil { + if string(data[0:1]) == "<" { + // Assume this frame uses non-transparent-framing + return len(data), data, nil + } return 0, nil, err } end := length + i + 1 if len(data) >= end { - //Return the frame with the length removed + // Return the frame with the length removed return end, data[i+1 : end], nil } } diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc6587_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc6587_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/format/rfc6587_test.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/format/rfc6587_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -45,6 +45,30 @@ c.Assert(i, Equals, len(find)) } +func (s *FormatSuite) TestRFC6587_GetSplitFuncMultiSplitNonTransparentFraming(c *C) { + f := RFC6587{} + + find := []string{ + "<1> I am a test.", + "<2> I am a test 2.", + "<3> hahahah", + } + buf := new(bytes.Buffer) + for _, i := range find { + fmt.Fprintf(buf, "%s", i) + } + scanner := bufio.NewScanner(buf) + scanner.Split(f.GetSplitFunc()) + + i := 0 + for scanner.Scan() { + c.Assert(scanner.Text(), Equals, strings.Join(find, "")) + i++ + } + + c.Assert(i, Equals, 1) +} + func (s *FormatSuite) TestRFC6587_GetSplitBadSplit(c *C) { f := RFC6587{} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/handler.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/handler.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/handler.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/handler.go 2017-07-07 17:38:23.000000000 +0000 @@ -1,15 +1,15 @@ package syslog import ( - "github.com/jeromer/syslogparser" + "gopkg.in/mcuadros/go-syslog.v2/format" ) //The handler receive every syslog entry at Handle method type Handler interface { - Handle(syslogparser.LogParts, int64, error) + Handle(format.LogParts, int64, error) } -type LogPartsChannel chan syslogparser.LogParts +type LogPartsChannel chan format.LogParts //The ChannelHandler will send all the syslog entries into the given channel type ChannelHandler struct { @@ -30,6 +30,6 @@ } //Syslog entry receiver -func (h *ChannelHandler) Handle(logParts syslogparser.LogParts, messageLength int64, err error) { +func (h *ChannelHandler) Handle(logParts format.LogParts, messageLength int64, err error) { h.channel <- logParts } diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/handler_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/handler_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/handler_test.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/handler_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -1,8 +1,8 @@ package syslog import ( - "github.com/jeromer/syslogparser" . "gopkg.in/check.v1" + "gopkg.in/mcuadros/go-syslog.v2/format" ) type HandlerSuite struct{} @@ -10,7 +10,7 @@ var _ = Suite(&HandlerSuite{}) func (s *HandlerSuite) TestHandle(c *C) { - logPart := syslogparser.LogParts{"tag": "foo"} + logPart := format.LogParts{"tag": "foo"} channel := make(LogPartsChannel, 1) handler := NewChannelHandler(channel) diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/LICENSE golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/LICENSE --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/LICENSE 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,23 @@ +Copyright (c) 2013, Jérôme Renard +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/README.md golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/README.md --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/README.md 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/README.md 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,4 @@ +Syslogparser +============ + +This is a fork for [github.com/jeromer/syslogparser](https://github.com/jeromer/syslogparser), since this library is intensively used by `go-syslog`, now is integrated as a `internal` package. diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc3164/example_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc3164/example_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc3164/example_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc3164/example_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,20 @@ +package rfc3164_test + +import ( + "fmt" + + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164" +) + +func ExampleNewParser() { + b := "<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8" + buff := []byte(b) + + p := rfc3164.NewParser(buff) + err := p.Parse() + if err != nil { + panic(err) + } + + fmt.Println(p.Dump()) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc3164/rfc3164.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc3164/rfc3164.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc3164/rfc3164.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc3164/rfc3164.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,263 @@ +package rfc3164 + +import ( + "bytes" + "time" + + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser" +) + +type Parser struct { + buff []byte + cursor int + l int + priority syslogparser.Priority + version int + header header + message rfc3164message + location *time.Location + skipTag bool +} + +type header struct { + timestamp time.Time + hostname string +} + +type rfc3164message struct { + tag string + content string +} + +func NewParser(buff []byte) *Parser { + return &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + location: time.UTC, + } +} + +func (p *Parser) Location(location *time.Location) { + p.location = location +} + +func (p *Parser) Parse() error { + pri, err := p.parsePriority() + if err != nil { + return err + } + + tcursor := p.cursor + hdr, err := p.parseHeader() + if err == syslogparser.ErrTimestampUnknownFormat { + // RFC3164 sec 4.3.2. + hdr.timestamp = time.Now().Round(time.Second) + // No tag processing should be done + p.skipTag = true + // Reset cursor for content read + p.cursor = tcursor + } else if err != nil { + return err + } else { + p.cursor++ + } + + msg, err := p.parsemessage() + if err != syslogparser.ErrEOL { + return err + } + + p.priority = pri + p.version = syslogparser.NO_VERSION + p.header = hdr + p.message = msg + + return nil +} + +func (p *Parser) Dump() syslogparser.LogParts { + return syslogparser.LogParts{ + "timestamp": p.header.timestamp, + "hostname": p.header.hostname, + "tag": p.message.tag, + "content": p.message.content, + "priority": p.priority.P, + "facility": p.priority.F.Value, + "severity": p.priority.S.Value, + } +} + +func (p *Parser) parsePriority() (syslogparser.Priority, error) { + return syslogparser.ParsePriority(p.buff, &p.cursor, p.l) +} + +func (p *Parser) parseHeader() (header, error) { + hdr := header{} + var err error + + ts, err := p.parseTimestamp() + if err != nil { + return hdr, err + } + + hostname, err := p.parseHostname() + if err != nil { + return hdr, err + } + + hdr.timestamp = ts + hdr.hostname = hostname + + return hdr, nil +} + +func (p *Parser) parsemessage() (rfc3164message, error) { + msg := rfc3164message{} + var err error + + if !p.skipTag { + tag, err := p.parseTag() + if err != nil { + return msg, err + } + msg.tag = tag + } + + content, err := p.parseContent() + if err != syslogparser.ErrEOL { + return msg, err + } + + msg.content = content + + return msg, err +} + +// https://tools.ietf.org/html/rfc3164#section-4.1.2 +func (p *Parser) parseTimestamp() (time.Time, error) { + var ts time.Time + var err error + var tsFmtLen int + var sub []byte + + tsFmts := []string{ + "Jan 02 15:04:05", + "Jan 2 15:04:05", + } + + found := false + for _, tsFmt := range tsFmts { + tsFmtLen = len(tsFmt) + + if p.cursor+tsFmtLen > p.l { + continue + } + + sub = p.buff[p.cursor : tsFmtLen+p.cursor] + ts, err = time.ParseInLocation(tsFmt, string(sub), p.location) + if err == nil { + found = true + break + } + } + + if !found { + p.cursor = tsFmtLen + + // XXX : If the timestamp is invalid we try to push the cursor one byte + // XXX : further, in case it is a space + if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') { + p.cursor++ + } + + return ts, syslogparser.ErrTimestampUnknownFormat + } + + fixTimestampIfNeeded(&ts) + + p.cursor += tsFmtLen + + if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') { + p.cursor++ + } + + return ts, nil +} + +func (p *Parser) parseHostname() (string, error) { + return syslogparser.ParseHostname(p.buff, &p.cursor, p.l) +} + +// http://tools.ietf.org/html/rfc3164#section-4.1.3 +func (p *Parser) parseTag() (string, error) { + var b byte + var endOfTag bool + var bracketOpen bool + var tag []byte + var err error + var found bool + + from := p.cursor + + for { + if p.cursor == p.l { + // no tag found, reset cursor for content + p.cursor = from + return "", nil + } + + b = p.buff[p.cursor] + bracketOpen = (b == '[') + endOfTag = (b == ':' || b == ' ') + + // XXX : parse PID ? + if bracketOpen { + tag = p.buff[from:p.cursor] + found = true + } + + if endOfTag { + if !found { + tag = p.buff[from:p.cursor] + found = true + } + + p.cursor++ + break + } + + p.cursor++ + } + + if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') { + p.cursor++ + } + + return string(tag), err +} + +func (p *Parser) parseContent() (string, error) { + if p.cursor > p.l { + return "", syslogparser.ErrEOL + } + + content := bytes.Trim(p.buff[p.cursor:p.l], " ") + p.cursor += len(content) + + return string(content), syslogparser.ErrEOL +} + +func fixTimestampIfNeeded(ts *time.Time) { + now := time.Now() + y := ts.Year() + + if ts.Year() == 0 { + y = now.Year() + } + + newTs := time.Date(y, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(), + ts.Second(), ts.Nanosecond(), ts.Location()) + + *ts = newTs +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc3164/rfc3164_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc3164/rfc3164_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc3164/rfc3164_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc3164/rfc3164_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,337 @@ +package rfc3164 + +import ( + "bytes" + "testing" + "time" + + . "gopkg.in/check.v1" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser" +) + +// Hooks up gocheck into the gotest runner. +func Test(t *testing.T) { TestingT(t) } + +type Rfc3164TestSuite struct { +} + +var ( + _ = Suite(&Rfc3164TestSuite{}) + + // XXX : corresponds to the length of the last tried timestamp format + // XXX : Jan 2 15:04:05 + lastTriedTimestampLen = 15 +) + +func (s *Rfc3164TestSuite) TestParser_Valid(c *C) { + buff := []byte("<34>Oct 11 22:14:15 mymachine very.large.syslog.message.tag: 'su root' failed for lonvick on /dev/pts/8") + + p := NewParser(buff) + expectedP := &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + location: time.UTC, + } + + c.Assert(p, DeepEquals, expectedP) + + err := p.Parse() + c.Assert(err, IsNil) + + now := time.Now() + + obtained := p.Dump() + expected := syslogparser.LogParts{ + "timestamp": time.Date(now.Year(), time.October, 11, 22, 14, 15, 0, time.UTC), + "hostname": "mymachine", + "tag": "very.large.syslog.message.tag", + "content": "'su root' failed for lonvick on /dev/pts/8", + "priority": 34, + "facility": 4, + "severity": 2, + } + + c.Assert(obtained, DeepEquals, expected) +} + +func (s *Rfc3164TestSuite) TestParser_ValidNoTag(c *C) { + buff := []byte("<34>Oct 11 22:14:15 mymachine singleword") + + p := NewParser(buff) + expectedP := &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + location: time.UTC, + } + + c.Assert(p, DeepEquals, expectedP) + + err := p.Parse() + c.Assert(err, IsNil) + + now := time.Now() + + obtained := p.Dump() + expected := syslogparser.LogParts{ + "timestamp": time.Date(now.Year(), time.October, 11, 22, 14, 15, 0, time.UTC), + "hostname": "mymachine", + "tag": "", + "content": "singleword", + "priority": 34, + "facility": 4, + "severity": 2, + } + + c.Assert(obtained, DeepEquals, expected) +} + +// RFC 3164 section 4.3.2 +func (s *Rfc3164TestSuite) TestParser_NoTimestamp(c *C) { + buff := []byte("<14>INFO leaving (1) step postscripts") + + p := NewParser(buff) + expectedP := &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + location: time.UTC, + } + + c.Assert(p, DeepEquals, expectedP) + + err := p.Parse() + c.Assert(err, IsNil) + + now := time.Now() + + obtained := p.Dump() + obtained["timestamp"] = now // XXX: Need to mock out time to test this fully + expected := syslogparser.LogParts{ + "timestamp": now, + "hostname": "", + "tag": "", + "content": "INFO leaving (1) step postscripts", + "priority": 14, + "facility": 1, + "severity": 6, + } + + c.Assert(obtained, DeepEquals, expected) +} + +func (s *Rfc3164TestSuite) TestParseHeader_Valid(c *C) { + buff := []byte("Oct 11 22:14:15 mymachine ") + now := time.Now() + hdr := header{ + timestamp: time.Date(now.Year(), time.October, 11, 22, 14, 15, 0, time.UTC), + hostname: "mymachine", + } + + s.assertRfc3164Header(c, hdr, buff, 25, nil) +} + +func (s *Rfc3164TestSuite) TestParseHeader_InvalidTimestamp(c *C) { + buff := []byte("Oct 34 32:72:82 mymachine ") + hdr := header{} + + s.assertRfc3164Header(c, hdr, buff, lastTriedTimestampLen+1, syslogparser.ErrTimestampUnknownFormat) +} + +func (s *Rfc3164TestSuite) TestParsemessage_Valid(c *C) { + content := "foo bar baz blah quux" + buff := []byte("sometag[123]: " + content) + hdr := rfc3164message{ + tag: "sometag", + content: content, + } + + s.assertRfc3164message(c, hdr, buff, len(buff), syslogparser.ErrEOL) +} + +func (s *Rfc3164TestSuite) TestParseTimestamp_Invalid(c *C) { + buff := []byte("Oct 34 32:72:82") + ts := new(time.Time) + + s.assertTimestamp(c, *ts, buff, lastTriedTimestampLen, syslogparser.ErrTimestampUnknownFormat) +} + +func (s *Rfc3164TestSuite) TestParseTimestamp_TrailingSpace(c *C) { + // XXX : no year specified. Assumed current year + // XXX : no timezone specified. Assume UTC + buff := []byte("Oct 11 22:14:15 ") + + now := time.Now() + ts := time.Date(now.Year(), time.October, 11, 22, 14, 15, 0, time.UTC) + + s.assertTimestamp(c, ts, buff, len(buff), nil) +} + +func (s *Rfc3164TestSuite) TestParseTimestamp_OneDigitForMonths(c *C) { + // XXX : no year specified. Assumed current year + // XXX : no timezone specified. Assume UTC + buff := []byte("Oct 1 22:14:15") + + now := time.Now() + ts := time.Date(now.Year(), time.October, 1, 22, 14, 15, 0, time.UTC) + + s.assertTimestamp(c, ts, buff, len(buff), nil) +} + +func (s *Rfc3164TestSuite) TestParseTimestamp_Valid(c *C) { + // XXX : no year specified. Assumed current year + // XXX : no timezone specified. Assume UTC + buff := []byte("Oct 11 22:14:15") + + now := time.Now() + ts := time.Date(now.Year(), time.October, 11, 22, 14, 15, 0, time.UTC) + + s.assertTimestamp(c, ts, buff, len(buff), nil) +} + +func (s *Rfc3164TestSuite) TestParseTag_Pid(c *C) { + buff := []byte("apache2[10]:") + tag := "apache2" + + s.assertTag(c, tag, buff, len(buff), nil) +} + +func (s *Rfc3164TestSuite) TestParseTag_NoPid(c *C) { + buff := []byte("apache2:") + tag := "apache2" + + s.assertTag(c, tag, buff, len(buff), nil) +} + +func (s *Rfc3164TestSuite) TestParseTag_TrailingSpace(c *C) { + buff := []byte("apache2: ") + tag := "apache2" + + s.assertTag(c, tag, buff, len(buff), nil) +} + +func (s *Rfc3164TestSuite) TestParseTag_NoTag(c *C) { + buff := []byte("apache2") + tag := "" + + s.assertTag(c, tag, buff, 0, nil) +} + +func (s *Rfc3164TestSuite) TestParseContent_Valid(c *C) { + buff := []byte(" foo bar baz quux ") + content := string(bytes.Trim(buff, " ")) + + p := NewParser(buff) + obtained, err := p.parseContent() + c.Assert(err, Equals, syslogparser.ErrEOL) + c.Assert(obtained, Equals, content) + c.Assert(p.cursor, Equals, len(content)) +} + +func (s *Rfc3164TestSuite) BenchmarkParseTimestamp(c *C) { + buff := []byte("Oct 11 22:14:15") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parseTimestamp() + if err != nil { + panic(err) + } + + p.cursor = 0 + } +} + +func (s *Rfc3164TestSuite) BenchmarkParseHostname(c *C) { + buff := []byte("gimli.local") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parseHostname() + if err != nil { + panic(err) + } + + p.cursor = 0 + } +} + +func (s *Rfc3164TestSuite) BenchmarkParseTag(c *C) { + buff := []byte("apache2[10]:") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parseTag() + if err != nil { + panic(err) + } + + p.cursor = 0 + } +} + +func (s *Rfc3164TestSuite) BenchmarkParseHeader(c *C) { + buff := []byte("Oct 11 22:14:15 mymachine ") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parseHeader() + if err != nil { + panic(err) + } + + p.cursor = 0 + } +} + +func (s *Rfc3164TestSuite) BenchmarkParsemessage(c *C) { + buff := []byte("sometag[123]: foo bar baz blah quux") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parsemessage() + if err != syslogparser.ErrEOL { + panic(err) + } + + p.cursor = 0 + } +} + +func (s *Rfc3164TestSuite) assertTimestamp(c *C, ts time.Time, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseTimestamp() + c.Assert(obtained, Equals, ts) + c.Assert(p.cursor, Equals, expC) + c.Assert(err, Equals, e) +} + +func (s *Rfc3164TestSuite) assertTag(c *C, t string, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseTag() + c.Assert(obtained, Equals, t) + c.Assert(p.cursor, Equals, expC) + c.Assert(err, Equals, e) +} + +func (s *Rfc3164TestSuite) assertRfc3164Header(c *C, hdr header, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseHeader() + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, hdr) + c.Assert(p.cursor, Equals, expC) +} + +func (s *Rfc3164TestSuite) assertRfc3164message(c *C, msg rfc3164message, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parsemessage() + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, msg) + c.Assert(p.cursor, Equals, expC) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc5424/example_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc5424/example_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc5424/example_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc5424/example_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,20 @@ +package rfc5424_test + +import ( + "fmt" + + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424" +) + +func ExampleNewParser() { + b := `<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] An application event log entry...` + buff := []byte(b) + + p := rfc5424.NewParser(buff) + err := p.Parse() + if err != nil { + panic(err) + } + + fmt.Println(p.Dump()) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc5424/rfc5424.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc5424/rfc5424.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc5424/rfc5424.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc5424/rfc5424.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,600 @@ +// Note to self : never try to code while looking after your kids +// The result might look like this : https://pbs.twimg.com/media/BXqSuYXIEAAscVA.png + +package rfc5424 + +import ( + "fmt" + "math" + "strconv" + "time" + + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser" +) + +const ( + NILVALUE = '-' +) + +var ( + ErrYearInvalid = &syslogparser.ParserError{"Invalid year in timestamp"} + ErrMonthInvalid = &syslogparser.ParserError{"Invalid month in timestamp"} + ErrDayInvalid = &syslogparser.ParserError{"Invalid day in timestamp"} + ErrHourInvalid = &syslogparser.ParserError{"Invalid hour in timestamp"} + ErrMinuteInvalid = &syslogparser.ParserError{"Invalid minute in timestamp"} + ErrSecondInvalid = &syslogparser.ParserError{"Invalid second in timestamp"} + ErrSecFracInvalid = &syslogparser.ParserError{"Invalid fraction of second in timestamp"} + ErrTimeZoneInvalid = &syslogparser.ParserError{"Invalid time zone in timestamp"} + ErrInvalidTimeFormat = &syslogparser.ParserError{"Invalid time format"} + ErrInvalidAppName = &syslogparser.ParserError{"Invalid app name"} + ErrInvalidProcId = &syslogparser.ParserError{"Invalid proc ID"} + ErrInvalidMsgId = &syslogparser.ParserError{"Invalid msg ID"} + ErrNoStructuredData = &syslogparser.ParserError{"No structured data"} +) + +type Parser struct { + buff []byte + cursor int + l int + header header + structuredData string + message string +} + +type header struct { + priority syslogparser.Priority + version int + timestamp time.Time + hostname string + appName string + procId string + msgId string +} + +type partialTime struct { + hour int + minute int + seconds int + secFrac float64 +} + +type fullTime struct { + pt partialTime + loc *time.Location +} + +type fullDate struct { + year int + month int + day int +} + +func NewParser(buff []byte) *Parser { + return &Parser{ + buff: buff, + cursor: 0, + l: len(buff), + } +} + +func (p *Parser) Location(location *time.Location) { + // Ignore as RFC5424 syslog always has a timezone +} + +func (p *Parser) Parse() error { + hdr, err := p.parseHeader() + if err != nil { + return err + } + + p.header = hdr + + sd, err := p.parseStructuredData() + if err != nil { + return err + } + + p.structuredData = sd + p.cursor++ + + if p.cursor < p.l { + p.message = string(p.buff[p.cursor:]) + } + + return nil +} + +func (p *Parser) Dump() syslogparser.LogParts { + return syslogparser.LogParts{ + "priority": p.header.priority.P, + "facility": p.header.priority.F.Value, + "severity": p.header.priority.S.Value, + "version": p.header.version, + "timestamp": p.header.timestamp, + "hostname": p.header.hostname, + "app_name": p.header.appName, + "proc_id": p.header.procId, + "msg_id": p.header.msgId, + "structured_data": p.structuredData, + "message": p.message, + } +} + +// HEADER = PRI VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID +func (p *Parser) parseHeader() (header, error) { + hdr := header{} + + pri, err := p.parsePriority() + if err != nil { + return hdr, err + } + + hdr.priority = pri + + ver, err := p.parseVersion() + if err != nil { + return hdr, err + } + hdr.version = ver + p.cursor++ + + ts, err := p.parseTimestamp() + if err != nil { + return hdr, err + } + + hdr.timestamp = ts + p.cursor++ + + host, err := p.parseHostname() + if err != nil { + return hdr, err + } + + hdr.hostname = host + p.cursor++ + + appName, err := p.parseAppName() + if err != nil { + return hdr, err + } + + hdr.appName = appName + p.cursor++ + + procId, err := p.parseProcId() + if err != nil { + return hdr, nil + } + + hdr.procId = procId + p.cursor++ + + msgId, err := p.parseMsgId() + if err != nil { + return hdr, nil + } + + hdr.msgId = msgId + p.cursor++ + + return hdr, nil +} + +func (p *Parser) parsePriority() (syslogparser.Priority, error) { + return syslogparser.ParsePriority(p.buff, &p.cursor, p.l) +} + +func (p *Parser) parseVersion() (int, error) { + return syslogparser.ParseVersion(p.buff, &p.cursor, p.l) +} + +// https://tools.ietf.org/html/rfc5424#section-6.2.3 +func (p *Parser) parseTimestamp() (time.Time, error) { + var ts time.Time + + if p.buff[p.cursor] == NILVALUE { + p.cursor++ + return ts, nil + } + + fd, err := parseFullDate(p.buff, &p.cursor, p.l) + if err != nil { + return ts, err + } + + if p.buff[p.cursor] != 'T' { + return ts, ErrInvalidTimeFormat + } + + p.cursor++ + + ft, err := parseFullTime(p.buff, &p.cursor, p.l) + if err != nil { + return ts, syslogparser.ErrTimestampUnknownFormat + } + + nSec, err := toNSec(ft.pt.secFrac) + if err != nil { + return ts, err + } + + ts = time.Date( + fd.year, + time.Month(fd.month), + fd.day, + ft.pt.hour, + ft.pt.minute, + ft.pt.seconds, + nSec, + ft.loc, + ) + + return ts, nil +} + +// HOSTNAME = NILVALUE / 1*255PRINTUSASCII +func (p *Parser) parseHostname() (string, error) { + return syslogparser.ParseHostname(p.buff, &p.cursor, p.l) +} + +// APP-NAME = NILVALUE / 1*48PRINTUSASCII +func (p *Parser) parseAppName() (string, error) { + return parseUpToLen(p.buff, &p.cursor, p.l, 48, ErrInvalidAppName) +} + +// PROCID = NILVALUE / 1*128PRINTUSASCII +func (p *Parser) parseProcId() (string, error) { + return parseUpToLen(p.buff, &p.cursor, p.l, 128, ErrInvalidProcId) +} + +// MSGID = NILVALUE / 1*32PRINTUSASCII +func (p *Parser) parseMsgId() (string, error) { + return parseUpToLen(p.buff, &p.cursor, p.l, 32, ErrInvalidMsgId) +} + +func (p *Parser) parseStructuredData() (string, error) { + return parseStructuredData(p.buff, &p.cursor, p.l) +} + +// ---------------------------------------------- +// https://tools.ietf.org/html/rfc5424#section-6 +// ---------------------------------------------- + +// XXX : bind them to Parser ? + +// FULL-DATE : DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY +func parseFullDate(buff []byte, cursor *int, l int) (fullDate, error) { + var fd fullDate + + year, err := parseYear(buff, cursor, l) + if err != nil { + return fd, err + } + + if buff[*cursor] != '-' { + return fd, syslogparser.ErrTimestampUnknownFormat + } + + *cursor++ + + month, err := parseMonth(buff, cursor, l) + if err != nil { + return fd, err + } + + if buff[*cursor] != '-' { + return fd, syslogparser.ErrTimestampUnknownFormat + } + + *cursor++ + + day, err := parseDay(buff, cursor, l) + if err != nil { + return fd, err + } + + fd = fullDate{ + year: year, + month: month, + day: day, + } + + return fd, nil +} + +// DATE-FULLYEAR = 4DIGIT +func parseYear(buff []byte, cursor *int, l int) (int, error) { + yearLen := 4 + + if *cursor+yearLen > l { + return 0, syslogparser.ErrEOL + } + + // XXX : we do not check for a valid year (ie. 1999, 2013 etc) + // XXX : we only checks the format is correct + sub := string(buff[*cursor : *cursor+yearLen]) + + *cursor += yearLen + + year, err := strconv.Atoi(sub) + if err != nil { + return 0, ErrYearInvalid + } + + return year, nil +} + +// DATE-MONTH = 2DIGIT ; 01-12 +func parseMonth(buff []byte, cursor *int, l int) (int, error) { + return syslogparser.Parse2Digits(buff, cursor, l, 1, 12, ErrMonthInvalid) +} + +// DATE-MDAY = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year +func parseDay(buff []byte, cursor *int, l int) (int, error) { + // XXX : this is a relaxed constraint + // XXX : we do not check if valid regarding February or leap years + // XXX : we only checks that day is in range [01 -> 31] + // XXX : in other words this function will not rant if you provide Feb 31th + return syslogparser.Parse2Digits(buff, cursor, l, 1, 31, ErrDayInvalid) +} + +// FULL-TIME = PARTIAL-TIME TIME-OFFSET +func parseFullTime(buff []byte, cursor *int, l int) (fullTime, error) { + var loc = new(time.Location) + var ft fullTime + + pt, err := parsePartialTime(buff, cursor, l) + if err != nil { + return ft, err + } + + loc, err = parseTimeOffset(buff, cursor, l) + if err != nil { + return ft, err + } + + ft = fullTime{ + pt: pt, + loc: loc, + } + + return ft, nil +} + +// PARTIAL-TIME = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND[TIME-SECFRAC] +func parsePartialTime(buff []byte, cursor *int, l int) (partialTime, error) { + var pt partialTime + + hour, minute, err := getHourMinute(buff, cursor, l) + if err != nil { + return pt, err + } + + if buff[*cursor] != ':' { + return pt, ErrInvalidTimeFormat + } + + *cursor++ + + // ---- + + seconds, err := parseSecond(buff, cursor, l) + if err != nil { + return pt, err + } + + pt = partialTime{ + hour: hour, + minute: minute, + seconds: seconds, + } + + // ---- + + if buff[*cursor] != '.' { + return pt, nil + } + + *cursor++ + + secFrac, err := parseSecFrac(buff, cursor, l) + if err != nil { + return pt, nil + } + pt.secFrac = secFrac + + return pt, nil +} + +// TIME-HOUR = 2DIGIT ; 00-23 +func parseHour(buff []byte, cursor *int, l int) (int, error) { + return syslogparser.Parse2Digits(buff, cursor, l, 0, 23, ErrHourInvalid) +} + +// TIME-MINUTE = 2DIGIT ; 00-59 +func parseMinute(buff []byte, cursor *int, l int) (int, error) { + return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrMinuteInvalid) +} + +// TIME-SECOND = 2DIGIT ; 00-59 +func parseSecond(buff []byte, cursor *int, l int) (int, error) { + return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrSecondInvalid) +} + +// TIME-SECFRAC = "." 1*6DIGIT +func parseSecFrac(buff []byte, cursor *int, l int) (float64, error) { + maxDigitLen := 6 + + max := *cursor + maxDigitLen + from := *cursor + to := from + + for to = from; to < max; to++ { + if to >= l { + break + } + + c := buff[to] + if !syslogparser.IsDigit(c) { + break + } + } + + sub := string(buff[from:to]) + if len(sub) == 0 { + return 0, ErrSecFracInvalid + } + + secFrac, err := strconv.ParseFloat("0."+sub, 64) + *cursor = to + if err != nil { + return 0, ErrSecFracInvalid + } + + return secFrac, nil +} + +// TIME-OFFSET = "Z" / TIME-NUMOFFSET +func parseTimeOffset(buff []byte, cursor *int, l int) (*time.Location, error) { + + if buff[*cursor] == 'Z' { + *cursor++ + return time.UTC, nil + } + + return parseNumericalTimeOffset(buff, cursor, l) +} + +// TIME-NUMOFFSET = ("+" / "-") TIME-HOUR ":" TIME-MINUTE +func parseNumericalTimeOffset(buff []byte, cursor *int, l int) (*time.Location, error) { + var loc = new(time.Location) + + sign := buff[*cursor] + + if (sign != '+') && (sign != '-') { + return loc, ErrTimeZoneInvalid + } + + *cursor++ + + hour, minute, err := getHourMinute(buff, cursor, l) + if err != nil { + return loc, err + } + + tzStr := fmt.Sprintf("%s%02d:%02d", string(sign), hour, minute) + tmpTs, err := time.Parse("-07:00", tzStr) + if err != nil { + return loc, err + } + + return tmpTs.Location(), nil +} + +func getHourMinute(buff []byte, cursor *int, l int) (int, int, error) { + hour, err := parseHour(buff, cursor, l) + if err != nil { + return 0, 0, err + } + + if buff[*cursor] != ':' { + return 0, 0, ErrInvalidTimeFormat + } + + *cursor++ + + minute, err := parseMinute(buff, cursor, l) + if err != nil { + return 0, 0, err + } + + return hour, minute, nil +} + +func toNSec(sec float64) (int, error) { + _, frac := math.Modf(sec) + fracStr := strconv.FormatFloat(frac, 'f', 9, 64) + fracInt, err := strconv.Atoi(fracStr[2:]) + if err != nil { + return 0, err + } + + return fracInt, nil +} + +// ------------------------------------------------ +// https://tools.ietf.org/html/rfc5424#section-6.3 +// ------------------------------------------------ + +func parseStructuredData(buff []byte, cursor *int, l int) (string, error) { + var sdData string + var found bool + + if *cursor >= l { + return "-", nil + } + + if buff[*cursor] == NILVALUE { + *cursor++ + return "-", nil + } + + if buff[*cursor] != '[' { + return sdData, ErrNoStructuredData + } + + from := *cursor + to := from + + for to = from; to < l; to++ { + if found { + break + } + + b := buff[to] + + if b == ']' { + switch t := to + 1; { + case t == l: + found = true + case t <= l && buff[t] == ' ': + found = true + } + } + } + + if found { + *cursor = to + return string(buff[from:to]), nil + } + + return sdData, ErrNoStructuredData +} + +func parseUpToLen(buff []byte, cursor *int, l int, maxLen int, e error) (string, error) { + var to int + var found bool + var result string + + max := *cursor + maxLen + + for to = *cursor; (to < max) && (to < l); to++ { + if buff[to] == ' ' { + found = true + break + } + } + + if found { + result = string(buff[*cursor:to]) + } + + *cursor = to + + if found { + return result, nil + } + + return "", e +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc5424/rfc5424_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc5424/rfc5424_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/rfc5424/rfc5424_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/rfc5424/rfc5424_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,872 @@ +package rfc5424 + +import ( + "fmt" + "testing" + "time" + + . "gopkg.in/check.v1" + "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser" +) + +// Hooks up gocheck into the gotest runner. +func Test(t *testing.T) { TestingT(t) } + +type Rfc5424TestSuite struct { +} + +var _ = Suite(&Rfc5424TestSuite{}) + +func (s *Rfc5424TestSuite) TestParser_Valid(c *C) { + fixtures := []string{ + // no STRUCTURED-DATA + "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - 'su root' failed for lonvick on /dev/pts/8", + "<165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts.", + // with STRUCTURED-DATA + `<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] An application event log entry...`, + // STRUCTURED-DATA Only + `<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource= "Application" eventID="1011"][examplePriority@32473 class="high"]`, + // STRUCTURED-DATA Only + `<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 `, + } + + tmpTs, err := time.Parse("-07:00", "-07:00") + c.Assert(err, IsNil) + + expected := []syslogparser.LogParts{ + syslogparser.LogParts{ + "priority": 34, + "facility": 4, + "severity": 2, + "version": 1, + "timestamp": time.Date(2003, time.October, 11, 22, 14, 15, 3*10e5, time.UTC), + "hostname": "mymachine.example.com", + "app_name": "su", + "proc_id": "-", + "msg_id": "ID47", + "structured_data": "-", + "message": "'su root' failed for lonvick on /dev/pts/8", + }, + syslogparser.LogParts{ + "priority": 165, + "facility": 20, + "severity": 5, + "version": 1, + "timestamp": time.Date(2003, time.August, 24, 5, 14, 15, 3*10e2, tmpTs.Location()), + "hostname": "192.0.2.1", + "app_name": "myproc", + "proc_id": "8710", + "msg_id": "-", + "structured_data": "-", + "message": "%% It's time to make the do-nuts.", + }, + syslogparser.LogParts{ + "priority": 165, + "facility": 20, + "severity": 5, + "version": 1, + "timestamp": time.Date(2003, time.October, 11, 22, 14, 15, 3*10e5, time.UTC), + "hostname": "mymachine.example.com", + "app_name": "evntslog", + "proc_id": "-", + "msg_id": "ID47", + "structured_data": `[exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"]`, + "message": "An application event log entry...", + }, + syslogparser.LogParts{ + "priority": 165, + "facility": 20, + "severity": 5, + "version": 1, + "timestamp": time.Date(2003, time.October, 11, 22, 14, 15, 3*10e5, time.UTC), + "hostname": "mymachine.example.com", + "app_name": "evntslog", + "proc_id": "-", + "msg_id": "ID47", + "structured_data": `[exampleSDID@32473 iut="3" eventSource= "Application" eventID="1011"][examplePriority@32473 class="high"]`, + "message": "", + }, + syslogparser.LogParts{ + "priority": 165, + "facility": 20, + "severity": 5, + "version": 1, + "timestamp": time.Date(2003, time.October, 11, 22, 14, 15, 3*10e5, time.UTC), + "hostname": "mymachine.example.com", + "app_name": "evntslog", + "proc_id": "-", + "msg_id": "ID47", + "structured_data": "-", + "message": "", + }, + } + + c.Assert(len(fixtures), Equals, len(expected)) + start := 0 + for i, buff := range fixtures { + expectedP := &Parser{ + buff: []byte(buff), + cursor: start, + l: len(buff), + } + + p := NewParser([]byte(buff)) + c.Assert(p, DeepEquals, expectedP) + + err := p.Parse() + c.Assert(err, IsNil) + + obtained := p.Dump() + for k, v := range obtained { + c.Assert(v, DeepEquals, expected[i][k]) + } + } +} + +func (s *Rfc5424TestSuite) TestParseHeader_Valid(c *C) { + ts := time.Date(2003, time.October, 11, 22, 14, 15, 3*10e5, time.UTC) + tsString := "2003-10-11T22:14:15.003Z" + hostname := "mymachine.example.com" + appName := "su" + procId := "123" + msgId := "ID47" + nilValue := string(NILVALUE) + headerFmt := "<165>1 %s %s %s %s %s " + + fixtures := []string{ + // HEADER complete + fmt.Sprintf(headerFmt, tsString, hostname, appName, procId, msgId), + // TIMESTAMP as NILVALUE + fmt.Sprintf(headerFmt, nilValue, hostname, appName, procId, msgId), + // HOSTNAME as NILVALUE + fmt.Sprintf(headerFmt, tsString, nilValue, appName, procId, msgId), + // APP-NAME as NILVALUE + fmt.Sprintf(headerFmt, tsString, hostname, nilValue, procId, msgId), + // PROCID as NILVALUE + fmt.Sprintf(headerFmt, tsString, hostname, appName, nilValue, msgId), + // MSGID as NILVALUE + fmt.Sprintf(headerFmt, tsString, hostname, appName, procId, nilValue), + } + + pri := syslogparser.Priority{ + P: 165, + F: syslogparser.Facility{Value: 20}, + S: syslogparser.Severity{Value: 5}, + } + + expected := []header{ + // HEADER complete + header{ + priority: pri, + version: 1, + timestamp: ts, + hostname: hostname, + appName: appName, + procId: procId, + msgId: msgId, + }, + // TIMESTAMP as NILVALUE + header{ + priority: pri, + version: 1, + timestamp: *new(time.Time), + hostname: hostname, + appName: appName, + procId: procId, + msgId: msgId, + }, + // HOSTNAME as NILVALUE + header{ + priority: pri, + version: 1, + timestamp: ts, + hostname: nilValue, + appName: appName, + procId: procId, + msgId: msgId, + }, + // APP-NAME as NILVALUE + header{ + priority: pri, + version: 1, + timestamp: ts, + hostname: hostname, + appName: nilValue, + procId: procId, + msgId: msgId, + }, + // PROCID as NILVALUE + header{ + priority: pri, + version: 1, + timestamp: ts, + hostname: hostname, + appName: appName, + procId: nilValue, + msgId: msgId, + }, + // MSGID as NILVALUE + header{ + priority: pri, + version: 1, + timestamp: ts, + hostname: hostname, + appName: appName, + procId: procId, + msgId: nilValue, + }, + } + + for i, f := range fixtures { + p := NewParser([]byte(f)) + obtained, err := p.parseHeader() + c.Assert(err, IsNil) + c.Assert(obtained, Equals, expected[i]) + c.Assert(p.cursor, Equals, len(f)) + } +} + +func (s *Rfc5424TestSuite) TestParseTimestamp_UTC(c *C) { + buff := []byte("1985-04-12T23:20:50.52Z") + ts := time.Date(1985, time.April, 12, 23, 20, 50, 52*10e6, time.UTC) + + s.assertTimestamp(c, ts, buff, 23, nil) +} + +func (s *Rfc5424TestSuite) TestParseTimestamp_NumericTimezone(c *C) { + tz := "-04:00" + buff := []byte("1985-04-12T19:20:50.52" + tz) + + tmpTs, err := time.Parse("-07:00", tz) + c.Assert(err, IsNil) + + ts := time.Date(1985, time.April, 12, 19, 20, 50, 52*10e6, tmpTs.Location()) + + s.assertTimestamp(c, ts, buff, len(buff), nil) +} + +func (s *Rfc5424TestSuite) TestParseTimestamp_MilliSeconds(c *C) { + buff := []byte("2003-10-11T22:14:15.003Z") + + ts := time.Date(2003, time.October, 11, 22, 14, 15, 3*10e5, time.UTC) + + s.assertTimestamp(c, ts, buff, len(buff), nil) +} + +func (s *Rfc5424TestSuite) TestParseTimestamp_MicroSeconds(c *C) { + tz := "-07:00" + buff := []byte("2003-08-24T05:14:15.000003" + tz) + + tmpTs, err := time.Parse("-07:00", tz) + c.Assert(err, IsNil) + + ts := time.Date(2003, time.August, 24, 5, 14, 15, 3*10e2, tmpTs.Location()) + + s.assertTimestamp(c, ts, buff, len(buff), nil) +} + +func (s *Rfc5424TestSuite) TestParseTimestamp_NanoSeconds(c *C) { + buff := []byte("2003-08-24T05:14:15.000000003-07:00") + ts := new(time.Time) + + s.assertTimestamp(c, *ts, buff, 26, syslogparser.ErrTimestampUnknownFormat) +} + +func (s *Rfc5424TestSuite) TestParseTimestamp_NilValue(c *C) { + buff := []byte("-") + ts := new(time.Time) + + s.assertTimestamp(c, *ts, buff, 1, nil) +} + +func (s *Rfc5424TestSuite) TestFindNextSpace_NoSpace(c *C) { + buff := []byte("aaaaaa") + + s.assertFindNextSpace(c, 0, buff, syslogparser.ErrNoSpace) +} + +func (s *Rfc5424TestSuite) TestFindNextSpace_SpaceFound(c *C) { + buff := []byte("foo bar baz") + + s.assertFindNextSpace(c, 4, buff, nil) +} + +func (s *Rfc5424TestSuite) TestParseYear_Invalid(c *C) { + buff := []byte("1a2b") + expected := 0 + + s.assertParseYear(c, expected, buff, 4, ErrYearInvalid) +} + +func (s *Rfc5424TestSuite) TestParseYear_TooShort(c *C) { + buff := []byte("123") + expected := 0 + + s.assertParseYear(c, expected, buff, 0, syslogparser.ErrEOL) +} + +func (s *Rfc5424TestSuite) TestParseYear_Valid(c *C) { + buff := []byte("2013") + expected := 2013 + + s.assertParseYear(c, expected, buff, 4, nil) +} + +func (s *Rfc5424TestSuite) TestParseMonth_InvalidString(c *C) { + buff := []byte("ab") + expected := 0 + + s.assertParseMonth(c, expected, buff, 2, ErrMonthInvalid) +} + +func (s *Rfc5424TestSuite) TestParseMonth_InvalidRange(c *C) { + buff := []byte("00") + expected := 0 + + s.assertParseMonth(c, expected, buff, 2, ErrMonthInvalid) + + // ---- + + buff = []byte("13") + + s.assertParseMonth(c, expected, buff, 2, ErrMonthInvalid) +} + +func (s *Rfc5424TestSuite) TestParseMonth_TooShort(c *C) { + buff := []byte("1") + expected := 0 + + s.assertParseMonth(c, expected, buff, 0, syslogparser.ErrEOL) +} + +func (s *Rfc5424TestSuite) TestParseMonth_Valid(c *C) { + buff := []byte("02") + expected := 2 + + s.assertParseMonth(c, expected, buff, 2, nil) +} + +func (s *Rfc5424TestSuite) TestParseDay_InvalidString(c *C) { + buff := []byte("ab") + expected := 0 + + s.assertParseDay(c, expected, buff, 2, ErrDayInvalid) +} + +func (s *Rfc5424TestSuite) TestParseDay_TooShort(c *C) { + buff := []byte("1") + expected := 0 + + s.assertParseDay(c, expected, buff, 0, syslogparser.ErrEOL) +} + +func (s *Rfc5424TestSuite) TestParseDay_InvalidRange(c *C) { + buff := []byte("00") + expected := 0 + + s.assertParseDay(c, expected, buff, 2, ErrDayInvalid) + + // ---- + + buff = []byte("32") + + s.assertParseDay(c, expected, buff, 2, ErrDayInvalid) +} + +func (s *Rfc5424TestSuite) TestParseDay_Valid(c *C) { + buff := []byte("02") + expected := 2 + + s.assertParseDay(c, expected, buff, 2, nil) +} + +func (s *Rfc5424TestSuite) TestParseFullDate_Invalid(c *C) { + buff := []byte("2013+10-28") + fd := fullDate{} + + s.assertParseFullDate(c, fd, buff, 4, syslogparser.ErrTimestampUnknownFormat) + + // --- + + buff = []byte("2013-10+28") + s.assertParseFullDate(c, fd, buff, 7, syslogparser.ErrTimestampUnknownFormat) +} + +func (s *Rfc5424TestSuite) TestParseFullDate_Valid(c *C) { + buff := []byte("2013-10-28") + fd := fullDate{ + year: 2013, + month: 10, + day: 28, + } + + s.assertParseFullDate(c, fd, buff, len(buff), nil) +} + +func (s *Rfc5424TestSuite) TestParseHour_InvalidString(c *C) { + buff := []byte("azer") + expected := 0 + + s.assertParseHour(c, expected, buff, 2, ErrHourInvalid) +} + +func (s *Rfc5424TestSuite) TestParseHour_TooShort(c *C) { + buff := []byte("1") + expected := 0 + + s.assertParseHour(c, expected, buff, 0, syslogparser.ErrEOL) +} + +func (s *Rfc5424TestSuite) TestParseHour_InvalidRange(c *C) { + buff := []byte("-1") + expected := 0 + + s.assertParseHour(c, expected, buff, 2, ErrHourInvalid) + + // ---- + + buff = []byte("24") + + s.assertParseHour(c, expected, buff, 2, ErrHourInvalid) +} + +func (s *Rfc5424TestSuite) TestParseHour_Valid(c *C) { + buff := []byte("12") + expected := 12 + + s.assertParseHour(c, expected, buff, 2, nil) +} + +func (s *Rfc5424TestSuite) TestParseMinute_InvalidString(c *C) { + buff := []byte("azer") + expected := 0 + + s.assertParseMinute(c, expected, buff, 2, ErrMinuteInvalid) +} + +func (s *Rfc5424TestSuite) TestParseMinute_TooShort(c *C) { + buff := []byte("1") + expected := 0 + + s.assertParseMinute(c, expected, buff, 0, syslogparser.ErrEOL) +} + +func (s *Rfc5424TestSuite) TestParseMinute_InvalidRange(c *C) { + buff := []byte("-1") + expected := 0 + + s.assertParseMinute(c, expected, buff, 2, ErrMinuteInvalid) + + // ---- + + buff = []byte("60") + + s.assertParseMinute(c, expected, buff, 2, ErrMinuteInvalid) +} + +func (s *Rfc5424TestSuite) TestParseMinute_Valid(c *C) { + buff := []byte("12") + expected := 12 + + s.assertParseMinute(c, expected, buff, 2, nil) +} + +func (s *Rfc5424TestSuite) TestParseSecond_InvalidString(c *C) { + buff := []byte("azer") + expected := 0 + + s.assertParseSecond(c, expected, buff, 2, ErrSecondInvalid) +} + +func (s *Rfc5424TestSuite) TestParseSecond_TooShort(c *C) { + buff := []byte("1") + expected := 0 + + s.assertParseSecond(c, expected, buff, 0, syslogparser.ErrEOL) +} + +func (s *Rfc5424TestSuite) TestParseSecond_InvalidRange(c *C) { + buff := []byte("-1") + expected := 0 + + s.assertParseSecond(c, expected, buff, 2, ErrSecondInvalid) + + // ---- + + buff = []byte("60") + + s.assertParseSecond(c, expected, buff, 2, ErrSecondInvalid) +} + +func (s *Rfc5424TestSuite) TestParseSecond_Valid(c *C) { + buff := []byte("12") + expected := 12 + + s.assertParseSecond(c, expected, buff, 2, nil) +} + +func (s *Rfc5424TestSuite) TestParseSecFrac_InvalidString(c *C) { + buff := []byte("azerty") + expected := 0.0 + + s.assertParseSecFrac(c, expected, buff, 0, ErrSecFracInvalid) +} + +func (s *Rfc5424TestSuite) TestParseSecFrac_NanoSeconds(c *C) { + buff := []byte("123456789") + expected := 0.123456 + + s.assertParseSecFrac(c, expected, buff, 6, nil) +} + +func (s *Rfc5424TestSuite) TestParseSecFrac_Valid(c *C) { + buff := []byte("0") + + expected := 0.0 + s.assertParseSecFrac(c, expected, buff, 1, nil) + + buff = []byte("52") + expected = 0.52 + s.assertParseSecFrac(c, expected, buff, 2, nil) + + buff = []byte("003") + expected = 0.003 + s.assertParseSecFrac(c, expected, buff, 3, nil) + + buff = []byte("000003") + expected = 0.000003 + s.assertParseSecFrac(c, expected, buff, 6, nil) +} + +func (s *Rfc5424TestSuite) TestParseNumericalTimeOffset_Valid(c *C) { + buff := []byte("+02:00") + cursor := 0 + l := len(buff) + tmpTs, err := time.Parse("-07:00", string(buff)) + c.Assert(err, IsNil) + + obtained, err := parseNumericalTimeOffset(buff, &cursor, l) + c.Assert(err, IsNil) + + expected := tmpTs.Location() + c.Assert(obtained, DeepEquals, expected) + + c.Assert(cursor, Equals, 6) +} + +func (s *Rfc5424TestSuite) TestParseTimeOffset_Valid(c *C) { + buff := []byte("Z") + cursor := 0 + l := len(buff) + + obtained, err := parseTimeOffset(buff, &cursor, l) + c.Assert(err, IsNil) + c.Assert(obtained, DeepEquals, time.UTC) + c.Assert(cursor, Equals, 1) +} + +func (s *Rfc5424TestSuite) TestGetHourMin_Valid(c *C) { + buff := []byte("12:34") + cursor := 0 + l := len(buff) + + expectedHour := 12 + expectedMinute := 34 + + obtainedHour, obtainedMinute, err := getHourMinute(buff, &cursor, l) + c.Assert(err, IsNil) + c.Assert(obtainedHour, Equals, expectedHour) + c.Assert(obtainedMinute, Equals, expectedMinute) + + c.Assert(cursor, Equals, l) +} + +func (s *Rfc5424TestSuite) TestParsePartialTime_Valid(c *C) { + buff := []byte("05:14:15.000003") + cursor := 0 + l := len(buff) + + obtained, err := parsePartialTime(buff, &cursor, l) + expected := partialTime{ + hour: 5, + minute: 14, + seconds: 15, + secFrac: 0.000003, + } + + c.Assert(err, IsNil) + c.Assert(obtained, DeepEquals, expected) + c.Assert(cursor, Equals, l) +} + +func (s *Rfc5424TestSuite) TestParseFullTime_Valid(c *C) { + tz := "-02:00" + buff := []byte("05:14:15.000003" + tz) + cursor := 0 + l := len(buff) + + tmpTs, err := time.Parse("-07:00", string(tz)) + c.Assert(err, IsNil) + + obtainedFt, err := parseFullTime(buff, &cursor, l) + expectedFt := fullTime{ + pt: partialTime{ + hour: 5, + minute: 14, + seconds: 15, + secFrac: 0.000003, + }, + loc: tmpTs.Location(), + } + + c.Assert(err, IsNil) + c.Assert(obtainedFt, DeepEquals, expectedFt) + c.Assert(cursor, Equals, 21) +} + +func (s *Rfc5424TestSuite) TestToNSec(c *C) { + fixtures := []float64{ + 0.52, + 0.003, + 0.000003, + } + + expected := []int{ + 520000000, + 3000000, + 3000, + } + + c.Assert(len(fixtures), Equals, len(expected)) + for i, f := range fixtures { + obtained, err := toNSec(f) + c.Assert(err, IsNil) + c.Assert(obtained, Equals, expected[i]) + } +} + +func (s *Rfc5424TestSuite) TestParseAppName_Valid(c *C) { + buff := []byte("su ") + appName := "su" + + s.assertParseAppName(c, appName, buff, 2, nil) +} + +func (s *Rfc5424TestSuite) TestParseAppName_TooLong(c *C) { + // > 48chars + buff := []byte("suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu ") + appName := "" + + s.assertParseAppName(c, appName, buff, 48, ErrInvalidAppName) +} + +func (s *Rfc5424TestSuite) TestParseProcId_Valid(c *C) { + buff := []byte("123foo ") + procId := "123foo" + + s.assertParseProcId(c, procId, buff, 6, nil) +} + +func (s *Rfc5424TestSuite) TestParseProcId_TooLong(c *C) { + // > 128chars + buff := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab ") + procId := "" + + s.assertParseProcId(c, procId, buff, 128, ErrInvalidProcId) +} + +func (s *Rfc5424TestSuite) TestParseMsgId_Valid(c *C) { + buff := []byte("123foo ") + procId := "123foo" + + s.assertParseMsgId(c, procId, buff, 6, nil) +} + +func (s *Rfc5424TestSuite) TestParseMsgId_TooLong(c *C) { + // > 32chars + buff := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ") + procId := "" + + s.assertParseMsgId(c, procId, buff, 32, ErrInvalidMsgId) +} + +func (s *Rfc5424TestSuite) TestParseStructuredData_NilValue(c *C) { + // > 32chars + buff := []byte("-") + sdData := "-" + + s.assertParseSdName(c, sdData, buff, 1, nil) +} + +func (s *Rfc5424TestSuite) TestParseStructuredData_SingleStructuredData(c *C) { + sdData := `[exampleSDID@32473 iut="3" eventSource="Application"eventID="1011"]` + buff := []byte(sdData) + + s.assertParseSdName(c, sdData, buff, len(buff), nil) +} + +func (s *Rfc5424TestSuite) TestParseStructuredData_MultipleStructuredData(c *C) { + sdData := `[exampleSDID@32473 iut="3" eventSource="Application"eventID="1011"][examplePriority@32473 class="high"]` + buff := []byte(sdData) + + s.assertParseSdName(c, sdData, buff, len(buff), nil) +} + +func (s *Rfc5424TestSuite) TestParseStructuredData_MultipleStructuredDataInvalid(c *C) { + a := `[exampleSDID@32473 iut="3" eventSource="Application"eventID="1011"]` + sdData := a + ` [examplePriority@32473 class="high"]` + buff := []byte(sdData) + + s.assertParseSdName(c, a, buff, len(a), nil) +} + +// ------------- + +func (s *Rfc5424TestSuite) BenchmarkParseTimestamp(c *C) { + buff := []byte("2003-08-24T05:14:15.000003-07:00") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parseTimestamp() + if err != nil { + panic(err) + } + + p.cursor = 0 + } +} + +func (s *Rfc5424TestSuite) BenchmarkParseHeader(c *C) { + buff := []byte("<165>1 2003-10-11T22:14:15.003Z mymachine.example.com su 123 ID47") + + p := NewParser(buff) + + for i := 0; i < c.N; i++ { + _, err := p.parseHeader() + if err != nil { + panic(err) + } + + p.cursor = 0 + } +} + +// ------------- + +func (s *Rfc5424TestSuite) assertTimestamp(c *C, ts time.Time, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseTimestamp() + c.Assert(err, Equals, e) + + tFmt := time.RFC3339Nano + c.Assert(obtained.Format(tFmt), Equals, ts.Format(tFmt)) + + c.Assert(p.cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertFindNextSpace(c *C, nextSpace int, b []byte, e error) { + obtained, err := syslogparser.FindNextSpace(b, 0, len(b)) + c.Assert(obtained, Equals, nextSpace) + c.Assert(err, Equals, e) +} + +func (s *Rfc5424TestSuite) assertParseYear(c *C, year int, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseYear(b, &cursor, len(b)) + c.Assert(obtained, Equals, year) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseMonth(c *C, month int, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseMonth(b, &cursor, len(b)) + c.Assert(obtained, Equals, month) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseDay(c *C, day int, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseDay(b, &cursor, len(b)) + c.Assert(obtained, Equals, day) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseFullDate(c *C, fd fullDate, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseFullDate(b, &cursor, len(b)) + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, fd) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseHour(c *C, hour int, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseHour(b, &cursor, len(b)) + c.Assert(obtained, Equals, hour) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseMinute(c *C, minute int, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseMinute(b, &cursor, len(b)) + c.Assert(obtained, Equals, minute) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseSecond(c *C, second int, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseSecond(b, &cursor, len(b)) + c.Assert(obtained, Equals, second) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseSecFrac(c *C, secFrac float64, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseSecFrac(b, &cursor, len(b)) + c.Assert(obtained, Equals, secFrac) + c.Assert(err, Equals, e) + c.Assert(cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseAppName(c *C, appName string, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseAppName() + + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, appName) + c.Assert(p.cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseProcId(c *C, procId string, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseProcId() + + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, procId) + c.Assert(p.cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseMsgId(c *C, msgId string, b []byte, expC int, e error) { + p := NewParser(b) + obtained, err := p.parseMsgId() + + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, msgId) + c.Assert(p.cursor, Equals, expC) +} + +func (s *Rfc5424TestSuite) assertParseSdName(c *C, sdData string, b []byte, expC int, e error) { + cursor := 0 + obtained, err := parseStructuredData(b, &cursor, len(b)) + + c.Assert(err, Equals, e) + c.Assert(obtained, Equals, sdData) + c.Assert(cursor, Equals, expC) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/syslogparser.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/syslogparser.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/syslogparser.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/syslogparser.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,206 @@ +package syslogparser + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +const ( + PRI_PART_START = '<' + PRI_PART_END = '>' + + NO_VERSION = -1 +) + +var ( + ErrEOL = &ParserError{"End of log line"} + ErrNoSpace = &ParserError{"No space found"} + + ErrPriorityNoStart = &ParserError{"No start char found for priority"} + ErrPriorityEmpty = &ParserError{"Priority field empty"} + ErrPriorityNoEnd = &ParserError{"No end char found for priority"} + ErrPriorityTooShort = &ParserError{"Priority field too short"} + ErrPriorityTooLong = &ParserError{"Priority field too long"} + ErrPriorityNonDigit = &ParserError{"Non digit found in priority"} + + ErrVersionNotFound = &ParserError{"Can not find version"} + + ErrTimestampUnknownFormat = &ParserError{"Timestamp format unknown"} +) + +type LogParser interface { + Parse() error + Dump() LogParts + Location(*time.Location) +} + +type ParserError struct { + ErrorString string +} + +type Priority struct { + P int + F Facility + S Severity +} + +type Facility struct { + Value int +} + +type Severity struct { + Value int +} + +type LogParts map[string]interface{} + +// https://tools.ietf.org/html/rfc3164#section-4.1 +func ParsePriority(buff []byte, cursor *int, l int) (Priority, error) { + pri := newPriority(0) + + if l <= 0 { + return pri, ErrPriorityEmpty + } + + if buff[*cursor] != PRI_PART_START { + return pri, ErrPriorityNoStart + } + + i := 1 + priDigit := 0 + + for i < l { + if i >= 5 { + return pri, ErrPriorityTooLong + } + + c := buff[i] + + if c == PRI_PART_END { + if i == 1 { + return pri, ErrPriorityTooShort + } + + *cursor = i + 1 + return newPriority(priDigit), nil + } + + if IsDigit(c) { + v, e := strconv.Atoi(string(c)) + if e != nil { + return pri, e + } + + priDigit = (priDigit * 10) + v + } else { + return pri, ErrPriorityNonDigit + } + + i++ + } + + return pri, ErrPriorityNoEnd +} + +// https://tools.ietf.org/html/rfc5424#section-6.2.2 +func ParseVersion(buff []byte, cursor *int, l int) (int, error) { + if *cursor >= l { + return NO_VERSION, ErrVersionNotFound + } + + c := buff[*cursor] + *cursor++ + + // XXX : not a version, not an error though as RFC 3164 does not support it + if !IsDigit(c) { + return NO_VERSION, nil + } + + v, e := strconv.Atoi(string(c)) + if e != nil { + *cursor-- + return NO_VERSION, e + } + + return v, nil +} + +func IsDigit(c byte) bool { + return c >= '0' && c <= '9' +} + +func newPriority(p int) Priority { + // The Priority value is calculated by first multiplying the Facility + // number by 8 and then adding the numerical value of the Severity. + + return Priority{ + P: p, + F: Facility{Value: p / 8}, + S: Severity{Value: p % 8}, + } +} + +func FindNextSpace(buff []byte, from int, l int) (int, error) { + var to int + + for to = from; to < l; to++ { + if buff[to] == ' ' { + to++ + return to, nil + } + } + + return 0, ErrNoSpace +} + +func Parse2Digits(buff []byte, cursor *int, l int, min int, max int, e error) (int, error) { + digitLen := 2 + + if *cursor+digitLen > l { + return 0, ErrEOL + } + + sub := string(buff[*cursor : *cursor+digitLen]) + + *cursor += digitLen + + i, err := strconv.Atoi(sub) + if err != nil { + return 0, e + } + + if i >= min && i <= max { + return i, nil + } + + return 0, e +} + +func ParseHostname(buff []byte, cursor *int, l int) (string, error) { + from := *cursor + var to int + + for to = from; to < l; to++ { + if buff[to] == ' ' { + break + } + } + + hostname := buff[from:to] + + *cursor = to + + return string(hostname), nil +} + +func ShowCursorPos(buff []byte, cursor int) { + fmt.Println(string(buff)) + padding := strings.Repeat("-", cursor) + fmt.Println(padding + "↑\n") +} + +func (err *ParserError) Error() string { + return err.ErrorString +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/syslogparser_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/syslogparser_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/internal/syslogparser/syslogparser_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/internal/syslogparser/syslogparser_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,173 @@ +package syslogparser + +import ( + "testing" + + . "gopkg.in/check.v1" +) + +// Hooks up gocheck into the gotest runner. +func Test(t *testing.T) { TestingT(t) } + +type CommonTestSuite struct { +} + +var _ = Suite(&CommonTestSuite{}) + +func (s *CommonTestSuite) TestParsePriority_Empty(c *C) { + pri := newPriority(0) + buff := []byte("") + start := 0 + + s.assertPriority(c, pri, buff, start, start, ErrPriorityEmpty) +} + +func (s *CommonTestSuite) TestParsePriority_NoStart(c *C) { + pri := newPriority(0) + buff := []byte("7>") + start := 0 + + s.assertPriority(c, pri, buff, start, start, ErrPriorityNoStart) +} + +func (s *CommonTestSuite) TestParsePriority_NoEnd(c *C) { + pri := newPriority(0) + buff := []byte("<77") + start := 0 + + s.assertPriority(c, pri, buff, start, start, ErrPriorityNoEnd) +} + +func (s *CommonTestSuite) TestParsePriority_TooShort(c *C) { + pri := newPriority(0) + buff := []byte("<>") + start := 0 + + s.assertPriority(c, pri, buff, start, start, ErrPriorityTooShort) +} + +func (s *CommonTestSuite) TestParsePriority_TooLong(c *C) { + pri := newPriority(0) + buff := []byte("<1233>") + start := 0 + + s.assertPriority(c, pri, buff, start, start, ErrPriorityTooLong) +} + +func (s *CommonTestSuite) TestParsePriority_NoDigits(c *C) { + pri := newPriority(0) + buff := []byte("<7a8>") + start := 0 + + s.assertPriority(c, pri, buff, start, start, ErrPriorityNonDigit) +} + +func (s *CommonTestSuite) TestParsePriority_Ok(c *C) { + pri := newPriority(190) + buff := []byte("<190>") + start := 0 + + s.assertPriority(c, pri, buff, start, start+5, nil) +} + +func (s *CommonTestSuite) TestNewPriority(c *C) { + obtained := newPriority(165) + + expected := Priority{ + P: 165, + F: Facility{Value: 20}, + S: Severity{Value: 5}, + } + + c.Assert(obtained, DeepEquals, expected) +} + +func (s *CommonTestSuite) TestParseVersion_NotFound(c *C) { + buff := []byte("<123>") + start := 5 + + s.assertVersion(c, NO_VERSION, buff, start, start, ErrVersionNotFound) +} + +func (s *CommonTestSuite) TestParseVersion_NonDigit(c *C) { + buff := []byte("<123>a") + start := 5 + + s.assertVersion(c, NO_VERSION, buff, start, start+1, nil) +} + +func (s *CommonTestSuite) TestParseVersion_Ok(c *C) { + buff := []byte("<123>1") + start := 5 + + s.assertVersion(c, 1, buff, start, start+1, nil) +} + +func (s *CommonTestSuite) TestParseHostname_Invalid(c *C) { + // XXX : no year specified. Assumed current year + // XXX : no timezone specified. Assume UTC + buff := []byte("foo name") + start := 0 + hostname := "foo" + + s.assertHostname(c, hostname, buff, start, 3, nil) +} + +func (s *CommonTestSuite) TestParseHostname_Valid(c *C) { + // XXX : no year specified. Assumed current year + // XXX : no timezone specified. Assume UTC + hostname := "ubuntu11.somehost.com" + buff := []byte(hostname + " ") + start := 0 + + s.assertHostname(c, hostname, buff, start, len(hostname), nil) +} + +func (s *CommonTestSuite) BenchmarkParsePriority(c *C) { + buff := []byte("<190>") + var start int + l := len(buff) + + for i := 0; i < c.N; i++ { + start = 0 + _, err := ParsePriority(buff, &start, l) + if err != nil { + panic(err) + } + } +} + +func (s *CommonTestSuite) BenchmarkParseVersion(c *C) { + buff := []byte("<123>1") + start := 5 + l := len(buff) + + for i := 0; i < c.N; i++ { + start = 0 + _, err := ParseVersion(buff, &start, l) + if err != nil { + panic(err) + } + } +} + +func (s *CommonTestSuite) assertPriority(c *C, p Priority, b []byte, cursor int, expC int, e error) { + obtained, err := ParsePriority(b, &cursor, len(b)) + c.Assert(obtained, DeepEquals, p) + c.Assert(cursor, Equals, expC) + c.Assert(err, Equals, e) +} + +func (s *CommonTestSuite) assertVersion(c *C, version int, b []byte, cursor int, expC int, e error) { + obtained, err := ParseVersion(b, &cursor, len(b)) + c.Assert(obtained, Equals, version) + c.Assert(cursor, Equals, expC) + c.Assert(err, Equals, e) +} + +func (s *CommonTestSuite) assertHostname(c *C, h string, b []byte, cursor int, expC int, e error) { + obtained, err := ParseHostname(b, &cursor, len(b)) + c.Assert(obtained, Equals, h) + c.Assert(cursor, Equals, expC) + c.Assert(err, Equals, e) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/README.md golang-gopkg-mcuadros-go-syslog.v2-2.2.1/README.md --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/README.md 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/README.md 2017-07-07 17:38:23.000000000 +0000 @@ -1,4 +1,4 @@ -go-syslog [![Build Status](https://travis-ci.org/mcuadros/go-syslog.png?branch=master)](https://travis-ci.org/mcuadros/go-syslog) [![GoDoc](http://godoc.org/github.com/mcuadros/go-syslog?status.png)](http://godoc.org/github.com/mcuadros/go-syslog) [![GitHub release](https://img.shields.io/github/release/mcuadros/go-syslog.svg)](https://github.com/mcuadros/go-syslog/releases) +go-syslog [![Build Status](https://travis-ci.org/mcuadros/go-syslog.png?branch=master)](https://travis-ci.org/mcuadros/go-syslog) [![GoDoc](http://godoc.org/github.com/mcuadros/go-syslog?status.png)](hhttps://godoc.org/gopkg.in/mcuadros/go-syslog.v2) [![GitHub release](https://img.shields.io/github/release/mcuadros/go-syslog.svg)](https://github.com/mcuadros/go-syslog/releases) ============================== Syslog server library for go, build easy your custom syslog server over UDP, TCP or Unix sockets using RFC3164, RFC6587 or RFC5424 diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/server_bench_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/server_bench_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/server_bench_test.go 1970-01-01 00:00:00.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/server_bench_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -0,0 +1,105 @@ +package syslog + +import ( + "bufio" + "io" + "net" + "testing" + "time" + + "gopkg.in/mcuadros/go-syslog.v2/format" +) + +type noopFormatter struct{} + +func (noopFormatter) Parse() error { + return nil +} + +func (noopFormatter) Dump() format.LogParts { + return format.LogParts{} +} + +func (noopFormatter) Location(*time.Location) {} + +func (n noopFormatter) GetParser(l []byte) format.LogParser { + return n +} + +func (n noopFormatter) GetSplitFunc() bufio.SplitFunc { + return nil +} + +type handlerCounter struct { + expected int + current int + done chan struct{} +} + +func (s *handlerCounter) Handle(logParts format.LogParts, msgLen int64, err error) { + s.current++ + if s.current == s.expected { + close(s.done) + } +} + +type fakePacketConn struct { + *io.PipeReader +} + +func (c *fakePacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { + n, err = c.PipeReader.Read(b) + return +} +func (c *fakePacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { + return 0, nil +} +func (c *fakePacketConn) Close() error { + return nil +} +func (c *fakePacketConn) LocalAddr() net.Addr { + return nil +} +func (c *fakePacketConn) SetDeadline(t time.Time) error { + return nil +} +func (c *fakePacketConn) SetReadDeadline(t time.Time) error { + return nil +} +func (c *fakePacketConn) SetWriteDeadline(t time.Time) error { + return nil +} + +func BenchmarkDatagramNoFormatting(b *testing.B) { + handler := &handlerCounter{expected: b.N, done: make(chan struct{})} + server := NewServer() + defer server.Kill() + server.SetFormat(noopFormatter{}) + server.SetHandler(handler) + reader, writer := io.Pipe() + server.goReceiveDatagrams(&fakePacketConn{PipeReader: reader}) + server.goParseDatagrams() + msg := []byte(exampleSyslog + "\n") + b.SetBytes(int64(len(msg))) + for i := 0; i < b.N; i++ { + writer.Write(msg) + } + <-handler.done +} + +func BenchmarkTCPNoFormatting(b *testing.B) { + handler := &handlerCounter{expected: b.N, done: make(chan struct{})} + server := NewServer() + defer server.Kill() + server.SetFormat(noopFormatter{}) + server.SetHandler(handler) + server.ListenTCP("127.0.0.1:0") + server.Boot() + conn, _ := net.DialTimeout("tcp", server.listeners[0].Addr().String(), time.Second) + msg := []byte(exampleSyslog + "\n") + b.SetBytes(int64(len(msg))) + for i := 0; i < b.N; i++ { + conn.Write(msg) + } + <-handler.done +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/server.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/server.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/server.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/server.go 2017-07-07 17:38:23.000000000 +0000 @@ -5,6 +5,7 @@ "crypto/tls" "errors" "net" + "strings" "sync" "time" @@ -29,7 +30,7 @@ type Server struct { listeners []net.Listener - connections []net.Conn + connections []net.PacketConn wait sync.WaitGroup doneTcp chan bool datagramChannel chan DatagramMessage @@ -38,11 +39,16 @@ lastError error readTimeoutMilliseconds int64 tlsPeerNameFunc TlsPeerNameFunc + datagramPool sync.Pool } //NewServer returns a new Server func NewServer() *Server { - return &Server{tlsPeerNameFunc: defaultTlsPeerName} + return &Server{tlsPeerNameFunc: defaultTlsPeerName, datagramPool: sync.Pool{ + New: func() interface{} { + return make([]byte, 65536) + }, + }} } //Sets the syslog format (RFC3164 or RFC5424 or RFC6587) @@ -252,6 +258,13 @@ logParts := parser.Dump() logParts["client"] = client + if logParts["hostname"] == "" && s.format == RFC3164 { + if i := strings.Index(client, ":"); i > 1 { + logParts["hostname"] = client[:i] + } else { + logParts["hostname"] = client + } + } logParts["tls_peer"] = tlsPeer s.handler.Handle(logParts, int64(len(line)), err) @@ -307,16 +320,12 @@ client string } -func (s *Server) goReceiveDatagrams(connection net.Conn) { - packetconn, ok := connection.(net.PacketConn) - if !ok { - panic("Connection is not a packet connection") - } +func (s *Server) goReceiveDatagrams(packetconn net.PacketConn) { s.wait.Add(1) go func() { defer s.wait.Done() for { - buf := make([]byte, 65536) + buf := s.datagramPool.Get().([]byte) n, addr, err := packetconn.ReadFrom(buf) if err == nil { // Ignore trailing control characters and NULs @@ -362,6 +371,7 @@ } else { s.parser(msg.message, msg.client, "") } + s.datagramPool.Put(msg.message[:cap(msg.message)]) } } }() diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/server_test.go golang-gopkg-mcuadros-go-syslog.v2-2.2.1/server_test.go --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/server_test.go 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/server_test.go 2017-07-07 17:38:23.000000000 +0000 @@ -7,8 +7,8 @@ "testing" "time" - "github.com/jeromer/syslogparser" . "gopkg.in/check.v1" + "gopkg.in/mcuadros/go-syslog.v2/format" ) func Test(t *testing.T) { TestingT(t) } @@ -18,6 +18,7 @@ var _ = Suite(&ServerSuite{}) var exampleSyslog = "<31>Dec 26 05:08:46 hostname tag[296]: content" +var exampleSyslogNoTSTagHost = "<14>INFO leaving (1) step postscripts" var exampleRFC5424Syslog = "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - 'su root' failed for lonvick on /dev/pts/8" func (s *ServerSuite) TestTailFile(c *C) { @@ -50,12 +51,12 @@ } type HandlerMock struct { - LastLogParts syslogparser.LogParts + LastLogParts format.LogParts LastMessageLength int64 LastError error } -func (s *HandlerMock) Handle(logParts syslogparser.LogParts, msgLen int64, err error) { +func (s *HandlerMock) Handle(logParts format.LogParts, msgLen int64, err error) { s.LastLogParts = logParts s.LastMessageLength = msgLen s.LastError = err @@ -166,6 +167,23 @@ c.Check(handler.LastError, IsNil) } +func (s *ServerSuite) TestUDP3164NoTag(c *C) { + handler := new(HandlerMock) + server := NewServer() + server.SetFormat(RFC3164) + server.SetHandler(handler) + server.SetTimeout(10) + server.goParseDatagrams() + server.datagramChannel <- DatagramMessage{[]byte(exampleSyslogNoTSTagHost), "127.0.0.1:45789"} + close(server.datagramChannel) + server.Wait() + c.Check(handler.LastLogParts["hostname"], Equals, "127.0.0.1") + c.Check(handler.LastLogParts["tag"], Equals, "") + c.Check(handler.LastLogParts["content"], Equals, "INFO leaving (1) step postscripts") + c.Check(handler.LastMessageLength, Equals, int64(len(exampleSyslogNoTSTagHost))) + c.Check(handler.LastError, IsNil) +} + func (s *ServerSuite) TestUDP6587(c *C) { handler := new(HandlerMock) server := NewServer() @@ -253,3 +271,58 @@ c.Check(handler.LastMessageLength, Equals, int64(len(exampleRFC5424Syslog))) c.Check(handler.LastError, IsNil) } + +type handlerSlow struct { + *handlerCounter + contents []string +} + +func (s *handlerSlow) Handle(logParts format.LogParts, msgLen int64, err error) { + if len(s.contents) == 0 { + time.Sleep(time.Second) + } + s.contents = append(s.contents, logParts["content"].(string)) + s.handlerCounter.Handle(logParts, msgLen, err) +} + +func (s *ServerSuite) TestUDPRace(c *C) { + handler := &handlerSlow{handlerCounter: &handlerCounter{expected: 3, done: make(chan struct{})}} + server := NewServer() + server.SetFormat(Automatic) + server.SetHandler(handler) + server.SetTimeout(10) + server.ListenUDP("127.0.0.1:0") + server.Boot() + conn, err := net.Dial("udp", server.connections[0].LocalAddr().String()) + c.Assert(err, IsNil) + _, err = conn.Write([]byte(exampleSyslog + "1")) + c.Assert(err, IsNil) + _, err = conn.Write([]byte(exampleSyslog + "2")) + c.Assert(err, IsNil) + _, err = conn.Write([]byte(exampleSyslog + "3")) + c.Assert(err, IsNil) + conn.Close() + <-handler.done + c.Check(handler.contents, DeepEquals, []string{"content1", "content2", "content3"}) +} + +func (s *ServerSuite) TestTCPRace(c *C) { + handler := &handlerSlow{handlerCounter: &handlerCounter{expected: 3, done: make(chan struct{})}} + server := NewServer() + server.SetFormat(Automatic) + server.SetHandler(handler) + server.SetTimeout(10) + server.ListenTCP("127.0.0.1:0") + server.Boot() + conn, err := net.Dial("tcp", server.listeners[0].Addr().String()) + c.Assert(err, IsNil) + _, err = conn.Write([]byte(exampleSyslog + "1\n")) + c.Assert(err, IsNil) + _, err = conn.Write([]byte(exampleSyslog + "2\n")) + c.Assert(err, IsNil) + _, err = conn.Write([]byte(exampleSyslog + "3\n")) + c.Assert(err, IsNil) + conn.Close() + <-handler.done + c.Check(handler.contents, DeepEquals, []string{"content1", "content2", "content3"}) +} diff -Nru golang-gopkg-mcuadros-go-syslog.v2-2.1.1/.travis.yml golang-gopkg-mcuadros-go-syslog.v2-2.2.1/.travis.yml --- golang-gopkg-mcuadros-go-syslog.v2-2.1.1/.travis.yml 2016-03-12 08:47:31.000000000 +0000 +++ golang-gopkg-mcuadros-go-syslog.v2-2.2.1/.travis.yml 2017-07-07 17:38:23.000000000 +0000 @@ -1,10 +1,22 @@ language: go go: - - 1.3 - 1.4 + - 1.5 + - 1.6 + - 1.7 - tip +matrix: + allow_failures: + - go: tip + +install: + - rm -rf $GOPATH/src/gopkg.in/mcuadros + - mkdir -p $GOPATH/src/gopkg.in/mcuadros + - ln -s $PWD $GOPATH/src/gopkg.in/mcuadros/go-syslog.v2 + - cd $GOPATH/src/gopkg.in/mcuadros/go-syslog.v2 && go get -v -t ./... + script: - - go test -v ./... + - cd $GOPATH/src/gopkg.in/mcuadros/go-syslog.v2 && go test -v ./... sudo: false