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  }