github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/syslog/internal/parser/rfc3164/parse.go (about) 1 package rfc3164 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog/internal/parser/utils" 8 ) 9 10 type RFC3164Option func(*RFC3164) 11 12 type RFC3164 struct { 13 PRI int 14 Timestamp time.Time 15 Hostname string 16 Tag string 17 Message string 18 PID string 19 // 20 len int 21 position int 22 buf []byte 23 useCurrentYear bool //If no year is specified in the timestamp, use the current year 24 strictHostname bool //If the hostname contains invalid characters or is not an IP, return an error 25 } 26 27 const PRI_MAX_LEN = 3 28 29 //Order is important: format with the most information must be first because we will stop on the first match 30 var VALID_TIMESTAMPS = []string{ 31 time.RFC3339, 32 "Jan 02 15:04:05 2006", 33 "Jan _2 15:04:05 2006", 34 "Jan 02 15:04:05", 35 "Jan _2 15:04:05", 36 } 37 38 func WithCurrentYear() RFC3164Option { 39 return func(r *RFC3164) { 40 r.useCurrentYear = true 41 } 42 } 43 44 func WithStrictHostname() RFC3164Option { 45 return func(r *RFC3164) { 46 r.strictHostname = true 47 } 48 } 49 50 func (r *RFC3164) 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 *RFC3164) parseTimestamp() error { 86 validTs := false 87 for _, layout := range VALID_TIMESTAMPS { 88 tsLen := len(layout) 89 if r.position+tsLen > r.len { 90 continue 91 } 92 t, err := time.Parse(layout, string(r.buf[r.position:r.position+tsLen])) 93 if err == nil { 94 validTs = true 95 r.Timestamp = t 96 r.position += tsLen 97 break 98 } 99 } 100 if !validTs { 101 return fmt.Errorf("timestamp is not valid") 102 } 103 if r.useCurrentYear { 104 if r.Timestamp.Year() == 0 { 105 r.Timestamp = time.Date(time.Now().Year(), r.Timestamp.Month(), r.Timestamp.Day(), r.Timestamp.Hour(), r.Timestamp.Minute(), r.Timestamp.Second(), r.Timestamp.Nanosecond(), r.Timestamp.Location()) 106 } 107 } 108 r.position++ 109 return nil 110 } 111 112 func (r *RFC3164) parseHostname() error { 113 hostname := []byte{} 114 for r.position < r.len { 115 c := r.buf[r.position] 116 if c == ' ' { 117 r.position++ 118 break 119 } 120 hostname = append(hostname, c) 121 r.position++ 122 } 123 if r.strictHostname { 124 if !utils.IsValidHostnameOrIP(string(hostname)) { 125 return fmt.Errorf("hostname is not valid") 126 } 127 } 128 if len(hostname) == 0 { 129 return fmt.Errorf("hostname is empty") 130 } 131 r.Hostname = string(hostname) 132 return nil 133 } 134 135 //We do not enforce tag len as quite a lot of syslog client send tags with more than 32 chars 136 func (r *RFC3164) parseTag() error { 137 tag := []byte{} 138 tmpPid := []byte{} 139 pidEnd := false 140 hasPid := false 141 for r.position < r.len { 142 c := r.buf[r.position] 143 if !utils.IsAlphaNumeric(c) { 144 break 145 } 146 tag = append(tag, c) 147 r.position++ 148 } 149 if len(tag) == 0 { 150 return fmt.Errorf("tag is empty") 151 } 152 r.Tag = string(tag) 153 154 if r.position == r.len { 155 return nil 156 } 157 158 c := r.buf[r.position] 159 if c == '[' { 160 hasPid = true 161 r.position++ 162 for r.position < r.len { 163 c = r.buf[r.position] 164 if c == ']' { 165 pidEnd = true 166 r.position++ 167 break 168 } 169 if c < '0' || c > '9' { 170 return fmt.Errorf("pid inside tag must be a number") 171 } 172 tmpPid = append(tmpPid, c) 173 r.position++ 174 } 175 } 176 177 if hasPid && !pidEnd { 178 return fmt.Errorf("pid inside tag must be closed with ']'") 179 } 180 181 if hasPid { 182 r.PID = string(tmpPid) 183 } 184 return nil 185 } 186 187 func (r *RFC3164) parseMessage() error { 188 err := r.parseTag() 189 if err != nil { 190 return err 191 } 192 193 if r.position == r.len { 194 return fmt.Errorf("message is empty") 195 } 196 197 c := r.buf[r.position] 198 199 if c == ':' { 200 r.position++ 201 } 202 203 for { 204 if r.position >= r.len { 205 return fmt.Errorf("message is empty") 206 } 207 c := r.buf[r.position] 208 if c != ' ' { 209 break 210 } 211 r.position++ 212 } 213 214 message := r.buf[r.position:r.len] 215 r.Message = string(message) 216 return nil 217 } 218 219 func (r *RFC3164) Parse(message []byte) error { 220 r.len = len(message) 221 if r.len == 0 { 222 return fmt.Errorf("message is empty") 223 } 224 r.buf = message 225 226 err := r.parsePRI() 227 if err != nil { 228 return err 229 } 230 231 err = r.parseTimestamp() 232 if err != nil { 233 return err 234 } 235 236 err = r.parseHostname() 237 if err != nil { 238 return err 239 } 240 241 err = r.parseMessage() 242 if err != nil { 243 return err 244 } 245 246 return nil 247 } 248 249 func NewRFC3164Parser(opts ...RFC3164Option) *RFC3164 { 250 r := &RFC3164{} 251 for _, opt := range opts { 252 opt(r) 253 } 254 return r 255 }