github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/syslog/internal/parser/rfc5424/parse.go (about) 1 package rfc5424 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog/internal/parser/utils" 8 ) 9 10 type RFC5424Option func(*RFC5424) 11 12 type RFC5424 struct { 13 PRI int 14 Timestamp time.Time 15 Hostname string 16 Tag string 17 Message string 18 PID string 19 MsgID string 20 // 21 len int 22 position int 23 buf []byte 24 useCurrentYear bool //If no year is specified in the timestamp, use the current year 25 strictHostname bool //If the hostname contains invalid characters or is not an IP, return an error 26 } 27 28 const PRI_MAX_LEN = 3 29 30 const NIL_VALUE = '-' 31 32 var VALID_TIMESTAMPS = []string{ 33 time.RFC3339, 34 } 35 36 const VALID_TIMESTAMP = time.RFC3339Nano 37 38 func WithCurrentYear() RFC5424Option { 39 return func(r *RFC5424) { 40 r.useCurrentYear = true 41 } 42 } 43 44 func WithStrictHostname() RFC5424Option { 45 return func(r *RFC5424) { 46 r.strictHostname = true 47 } 48 } 49 50 func (r *RFC5424) parsePRI() error { 51 52 pri := 0 53 54 if r.buf[r.position] != '<' { 55 return fmt.Errorf("PRI must start with '<'") 56 } 57 58 r.position++ 59 60 for r.position < r.len { 61 c := r.buf[r.position] 62 if c == '>' { 63 r.position++ 64 break 65 } 66 if c < '0' || c > '9' { 67 return fmt.Errorf("PRI must be a number") 68 } 69 pri = pri*10 + int(c-'0') 70 r.position++ 71 } 72 73 if pri > 999 { 74 return fmt.Errorf("PRI must be up to 3 characters long") 75 } 76 77 if r.position == r.len && r.buf[r.position-1] != '>' { 78 return fmt.Errorf("PRI must end with '>'") 79 } 80 81 r.PRI = pri 82 return nil 83 } 84 85 func (r *RFC5424) parseVersion() error { 86 if r.buf[r.position] != '1' { 87 return fmt.Errorf("version must be 1") 88 } 89 r.position += 2 90 if r.position >= r.len { 91 return fmt.Errorf("version must be followed by a space") 92 } 93 return nil 94 } 95 96 func (r *RFC5424) parseTimestamp() error { 97 98 timestamp := []byte{} 99 100 if r.buf[r.position] == NIL_VALUE { 101 r.Timestamp = time.Now().UTC().Round(0) 102 r.position += 2 103 return nil 104 } 105 106 for r.position < r.len { 107 c := r.buf[r.position] 108 if c == ' ' { 109 break 110 } 111 timestamp = append(timestamp, c) 112 r.position++ 113 } 114 115 if len(timestamp) == 0 { 116 return fmt.Errorf("timestamp is empty") 117 } 118 119 if r.position == r.len { 120 return fmt.Errorf("EOL after timestamp") 121 } 122 123 date, err := time.Parse(VALID_TIMESTAMP, string(timestamp)) 124 125 if err != nil { 126 return fmt.Errorf("timestamp is not valid") 127 } 128 129 r.Timestamp = date 130 131 r.position++ 132 133 if r.position >= r.len { 134 return fmt.Errorf("EOL after timestamp") 135 } 136 137 return nil 138 } 139 140 func (r *RFC5424) parseHostname() error { 141 if r.buf[r.position] == NIL_VALUE { 142 r.Hostname = "" 143 r.position += 2 144 return nil 145 } 146 147 hostname := []byte{} 148 for r.position < r.len { 149 c := r.buf[r.position] 150 if c == ' ' { 151 r.position++ 152 break 153 } 154 hostname = append(hostname, c) 155 r.position++ 156 } 157 if r.strictHostname { 158 if !utils.IsValidHostnameOrIP(string(hostname)) { 159 return fmt.Errorf("hostname is not valid") 160 } 161 } 162 if len(hostname) == 0 { 163 return fmt.Errorf("hostname is empty") 164 } 165 r.Hostname = string(hostname) 166 return nil 167 } 168 169 func (r *RFC5424) parseAppName() error { 170 if r.buf[r.position] == NIL_VALUE { 171 r.Tag = "" 172 r.position += 2 173 return nil 174 } 175 176 appname := []byte{} 177 for r.position < r.len { 178 c := r.buf[r.position] 179 if c == ' ' { 180 r.position++ 181 break 182 } 183 appname = append(appname, c) 184 r.position++ 185 } 186 187 if len(appname) == 0 { 188 return fmt.Errorf("appname is empty") 189 } 190 191 if len(appname) > 48 { 192 return fmt.Errorf("appname is too long") 193 } 194 195 r.Tag = string(appname) 196 return nil 197 } 198 199 func (r *RFC5424) parseProcID() error { 200 if r.buf[r.position] == NIL_VALUE { 201 r.PID = "" 202 r.position += 2 203 return nil 204 } 205 206 procid := []byte{} 207 for r.position < r.len { 208 c := r.buf[r.position] 209 if c == ' ' { 210 r.position++ 211 break 212 } 213 procid = append(procid, c) 214 r.position++ 215 } 216 217 if len(procid) == 0 { 218 return fmt.Errorf("procid is empty") 219 } 220 221 if len(procid) > 128 { 222 return fmt.Errorf("procid is too long") 223 } 224 225 r.PID = string(procid) 226 return nil 227 } 228 229 func (r *RFC5424) parseMsgID() error { 230 if r.buf[r.position] == NIL_VALUE { 231 r.MsgID = "" 232 r.position += 2 233 return nil 234 } 235 236 msgid := []byte{} 237 for r.position < r.len { 238 c := r.buf[r.position] 239 if c == ' ' { 240 r.position++ 241 break 242 } 243 msgid = append(msgid, c) 244 r.position++ 245 } 246 247 if len(msgid) == 0 { 248 return fmt.Errorf("msgid is empty") 249 } 250 251 if len(msgid) > 32 { 252 return fmt.Errorf("msgid is too long") 253 } 254 255 r.MsgID = string(msgid) 256 return nil 257 } 258 259 func (r *RFC5424) parseStructuredData() error { 260 done := false 261 if r.buf[r.position] == NIL_VALUE { 262 r.position += 2 263 return nil 264 } 265 if r.buf[r.position] != '[' { 266 return fmt.Errorf("structured data must start with '[' or be '-'") 267 } 268 prev := byte(0) 269 for r.position < r.len { 270 done = false 271 c := r.buf[r.position] 272 if c == ']' && prev != '\\' { 273 done = true 274 r.position++ 275 if r.position < r.len && r.buf[r.position] == ' ' { 276 break 277 } 278 } 279 prev = c 280 r.position++ 281 } 282 r.position++ 283 if !done { 284 return fmt.Errorf("structured data must end with ']'") 285 } 286 return nil 287 } 288 289 func (r *RFC5424) parseMessage() error { 290 if r.position == r.len { 291 return fmt.Errorf("message is empty") 292 } 293 294 message := []byte{} 295 296 for r.position < r.len { 297 c := r.buf[r.position] 298 message = append(message, c) 299 r.position++ 300 } 301 r.Message = string(message) 302 return nil 303 } 304 305 func (r *RFC5424) Parse(message []byte) error { 306 r.len = len(message) 307 if r.len == 0 { 308 return fmt.Errorf("syslog line is empty") 309 } 310 r.buf = message 311 312 err := r.parsePRI() 313 if err != nil { 314 return err 315 } 316 317 if r.position >= r.len { 318 return fmt.Errorf("EOL after PRI") 319 } 320 321 err = r.parseVersion() 322 if err != nil { 323 return err 324 } 325 326 if r.position >= r.len { 327 return fmt.Errorf("EOL after Version") 328 } 329 330 err = r.parseTimestamp() 331 if err != nil { 332 return err 333 } 334 335 if r.position >= r.len { 336 return fmt.Errorf("EOL after Timestamp") 337 } 338 339 err = r.parseHostname() 340 if err != nil { 341 return err 342 } 343 344 if r.position >= r.len { 345 return fmt.Errorf("EOL after hostname") 346 } 347 348 err = r.parseAppName() 349 if err != nil { 350 return err 351 } 352 353 if r.position >= r.len { 354 return fmt.Errorf("EOL after appname") 355 } 356 357 err = r.parseProcID() 358 if err != nil { 359 return err 360 } 361 362 if r.position >= r.len { 363 return fmt.Errorf("EOL after ProcID") 364 } 365 366 err = r.parseMsgID() 367 if err != nil { 368 return err 369 } 370 371 if r.position >= r.len { 372 return fmt.Errorf("EOL after MSGID") 373 } 374 375 err = r.parseStructuredData() 376 if err != nil { 377 return err 378 } 379 380 if r.position >= r.len { 381 return fmt.Errorf("EOL after SD") 382 } 383 384 err = r.parseMessage() 385 if err != nil { 386 return err 387 } 388 389 return nil 390 } 391 392 func NewRFC5424Parser(opts ...RFC5424Option) *RFC5424 { 393 r := &RFC5424{} 394 for _, opt := range opts { 395 opt(r) 396 } 397 return r 398 }