rfc3164.go (5570B)
1 package rfc3164 2 3 import ( 4 "bytes" 5 "os" 6 "time" 7 8 "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser" 9 ) 10 11 type Parser struct { 12 buff []byte 13 cursor int 14 l int 15 priority syslogparser.Priority 16 version int 17 header header 18 message rfc3164message 19 location *time.Location 20 skipTag bool 21 } 22 23 type header struct { 24 timestamp time.Time 25 hostname string 26 } 27 28 type rfc3164message struct { 29 tag string 30 content string 31 } 32 33 func NewParser(buff []byte) *Parser { 34 return &Parser{ 35 buff: buff, 36 cursor: 0, 37 l: len(buff), 38 location: time.UTC, 39 } 40 } 41 42 func (p *Parser) Location(location *time.Location) { 43 p.location = location 44 } 45 46 func (p *Parser) Parse() error { 47 tcursor := p.cursor 48 pri, err := p.parsePriority() 49 if err != nil { 50 // RFC3164 sec 4.3.3 51 p.priority = syslogparser.Priority{13, syslogparser.Facility{Value: 1}, syslogparser.Severity{Value: 5}} 52 p.cursor = tcursor 53 content, err := p.parseContent() 54 p.header.timestamp = time.Now().Round(time.Second) 55 if err != syslogparser.ErrEOL { 56 return err 57 } 58 p.message = rfc3164message{content: content} 59 return nil 60 } 61 62 tcursor = p.cursor 63 hdr, err := p.parseHeader() 64 if err == syslogparser.ErrTimestampUnknownFormat { 65 // RFC3164 sec 4.3.2. 66 hdr.timestamp = time.Now().Round(time.Second) 67 // No tag processing should be done 68 p.skipTag = true 69 // Reset cursor for content read 70 p.cursor = tcursor 71 } else if err != nil { 72 return err 73 } else { 74 p.cursor++ 75 } 76 77 msg, err := p.parsemessage() 78 if err != syslogparser.ErrEOL { 79 return err 80 } 81 82 p.priority = pri 83 p.version = syslogparser.NO_VERSION 84 p.header = hdr 85 p.message = msg 86 87 return nil 88 } 89 90 func (p *Parser) Dump() syslogparser.LogParts { 91 return syslogparser.LogParts{ 92 "timestamp": p.header.timestamp, 93 "hostname": p.header.hostname, 94 "tag": p.message.tag, 95 "content": p.message.content, 96 "priority": p.priority.P, 97 "facility": p.priority.F.Value, 98 "severity": p.priority.S.Value, 99 } 100 } 101 102 func (p *Parser) parsePriority() (syslogparser.Priority, error) { 103 return syslogparser.ParsePriority(p.buff, &p.cursor, p.l) 104 } 105 106 func (p *Parser) parseHeader() (header, error) { 107 hdr := header{} 108 var err error 109 110 ts, err := p.parseTimestamp() 111 if err != nil { 112 return hdr, err 113 } 114 115 hostname, err := p.parseHostname() 116 if err != nil { 117 return hdr, err 118 } 119 120 hdr.timestamp = ts 121 hdr.hostname = hostname 122 123 return hdr, nil 124 } 125 126 func (p *Parser) parsemessage() (rfc3164message, error) { 127 msg := rfc3164message{} 128 var err error 129 130 if !p.skipTag { 131 tag, err := p.parseTag() 132 if err != nil { 133 return msg, err 134 } 135 msg.tag = tag 136 } 137 138 content, err := p.parseContent() 139 if err != syslogparser.ErrEOL { 140 return msg, err 141 } 142 143 msg.content = content 144 145 return msg, err 146 } 147 148 // https://tools.ietf.org/html/rfc3164#section-4.1.2 149 func (p *Parser) parseTimestamp() (time.Time, error) { 150 var ts time.Time 151 var err error 152 var tsFmtLen int 153 var sub []byte 154 155 tsFmts := []string{ 156 time.Stamp, 157 time.RFC3339, 158 } 159 // if timestamps starts with numeric try formats with different order 160 // it is more likely that timestamp is in RFC3339 format then 161 if c := p.buff[p.cursor]; c > '0' && c < '9' { 162 tsFmts = []string{ 163 time.RFC3339, 164 time.Stamp, 165 } 166 } 167 168 found := false 169 for _, tsFmt := range tsFmts { 170 tsFmtLen = len(tsFmt) 171 172 if p.cursor+tsFmtLen > p.l { 173 continue 174 } 175 176 sub = p.buff[p.cursor : tsFmtLen+p.cursor] 177 ts, err = time.ParseInLocation(tsFmt, string(sub), p.location) 178 if err == nil { 179 found = true 180 break 181 } 182 } 183 184 if !found { 185 p.cursor = len(time.Stamp) 186 187 // XXX : If the timestamp is invalid we try to push the cursor one byte 188 // XXX : further, in case it is a space 189 if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') { 190 p.cursor++ 191 } 192 193 return ts, syslogparser.ErrTimestampUnknownFormat 194 } 195 196 fixTimestampIfNeeded(&ts) 197 198 p.cursor += tsFmtLen 199 200 if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') { 201 p.cursor++ 202 } 203 204 return ts, nil 205 } 206 207 func (p *Parser) parseHostname() (string, error) { 208 oldcursor := p.cursor 209 hostname, err := syslogparser.ParseHostname(p.buff, &p.cursor, p.l) 210 if err == nil && len(hostname) > 0 && string(hostname[len(hostname)-1]) == ":" { // not an hostname! we found a GNU implementation of syslog() 211 p.cursor = oldcursor - 1 212 myhostname, err := os.Hostname() 213 if err == nil { 214 return myhostname, nil 215 } 216 return "", nil 217 } 218 return hostname, err 219 } 220 221 // http://tools.ietf.org/html/rfc3164#section-4.1.3 222 func (p *Parser) parseTag() (string, error) { 223 var b byte 224 var endOfTag bool 225 var bracketOpen bool 226 var tag []byte 227 var err error 228 var found bool 229 230 from := p.cursor 231 232 for { 233 if p.cursor == p.l { 234 // no tag found, reset cursor for content 235 p.cursor = from 236 return "", nil 237 } 238 239 b = p.buff[p.cursor] 240 bracketOpen = (b == '[') 241 endOfTag = (b == ':' || b == ' ') 242 243 // XXX : parse PID ? 244 if bracketOpen { 245 tag = p.buff[from:p.cursor] 246 found = true 247 } 248 249 if endOfTag { 250 if !found { 251 tag = p.buff[from:p.cursor] 252 found = true 253 } 254 255 p.cursor++ 256 break 257 } 258 259 p.cursor++ 260 } 261 262 if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') { 263 p.cursor++ 264 } 265 266 return string(tag), err 267 } 268 269 func (p *Parser) parseContent() (string, error) { 270 if p.cursor > p.l { 271 return "", syslogparser.ErrEOL 272 } 273 274 content := bytes.Trim(p.buff[p.cursor:p.l], " ") 275 p.cursor += len(content) 276 277 return string(content), syslogparser.ErrEOL 278 } 279 280 func fixTimestampIfNeeded(ts *time.Time) { 281 now := time.Now() 282 y := ts.Year() 283 284 if ts.Year() == 0 { 285 y = now.Year() 286 } 287 288 newTs := time.Date(y, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(), 289 ts.Second(), ts.Nanosecond(), ts.Location()) 290 291 *ts = newTs 292 }