diff options
Diffstat (limited to 'vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser')
5 files changed, 1138 insertions, 0 deletions
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE new file mode 100644 index 000000000..3e2dbb6f8 --- /dev/null +++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE @@ -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 --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md new file mode 100644 index 000000000..ce59d376f --- /dev/null +++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md @@ -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 --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go new file mode 100644 index 000000000..486a0cb54 --- /dev/null +++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go @@ -0,0 +1,292 @@ +package rfc3164 + +import ( + "bytes" + "os" + "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 { + tcursor := p.cursor + pri, err := p.parsePriority() + if err != nil { + // RFC3164 sec 4.3.3 + p.priority = syslogparser.Priority{13, syslogparser.Facility{Value: 1}, syslogparser.Severity{Value: 5}} + p.cursor = tcursor + content, err := p.parseContent() + p.header.timestamp = time.Now().Round(time.Second) + if err != syslogparser.ErrEOL { + return err + } + p.message = rfc3164message{content: content} + return nil + } + + 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{ + time.Stamp, + time.RFC3339, + } + // if timestamps starts with numeric try formats with different order + // it is more likely that timestamp is in RFC3339 format then + if c := p.buff[p.cursor]; c > '0' && c < '9' { + tsFmts = []string{ + time.RFC3339, + time.Stamp, + } + } + + 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 = len(time.Stamp) + + // 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) { + oldcursor := p.cursor + hostname, err := syslogparser.ParseHostname(p.buff, &p.cursor, p.l) + if err == nil && len(hostname) > 0 && string(hostname[len(hostname)-1]) == ":" { // not an hostname! we found a GNU implementation of syslog() + p.cursor = oldcursor - 1 + myhostname, err := os.Hostname() + if err == nil { + return myhostname, nil + } + return "", nil + } + return hostname, err +} + +// 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 --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go new file mode 100644 index 000000000..bea8e5879 --- /dev/null +++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go @@ -0,0 +1,606 @@ +// 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.cursor >= p.l { + return ts, ErrInvalidTimeFormat + } + + 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.cursor >= p.l || 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 *cursor >= l || buff[*cursor] != '-' { + return fd, syslogparser.ErrTimestampUnknownFormat + } + + *cursor++ + + month, err := parseMonth(buff, cursor, l) + if err != nil { + return fd, err + } + + if *cursor >= l || 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 *cursor >= l || 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 *cursor >= l || 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 *cursor >= l || 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 *cursor >= l || 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]) + } else if to > max { + to = max // don't go past max + } + + *cursor = to + + if found { + return result, nil + } + + return "", e +} diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go new file mode 100644 index 000000000..7a10820c7 --- /dev/null +++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go @@ -0,0 +1,213 @@ +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"} + + ErrHostnameTooShort = &ParserError{"Hostname field too short"} +) + +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 + + if from >= l { + return "", ErrHostnameTooShort + } + + 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 +} |