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  }