-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrfc3164.go
179 lines (161 loc) · 3.97 KB
/
rfc3164.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package main
import (
"bytes"
"errors"
"strconv"
"time"
uni "unicode"
)
var space = []byte(" ")
// <PRI>Mmm dd hh:mm:ss HOSTNAME TAG MSG
// or <PRI>Mmm dd hh:mm:ss TAG MSG
// or <PRI>RFC3339 HOSTNAME TAG MSG
// or <PRI>RFC3339 TAG MSG
// TAG may be "TAG" or "TAG:" or "TAG[PID]" or "TAG[PID]:"
// HOSTNAME can be IPv4 (multiple "."), IPv6 (multiple ":"), or a hostname without domain.
// if <PRI> is not present, we assume it is just MSG
func pair2str(s1 []byte, s2 []byte) (string, string) {
return string(s1), string(s2)
}
type RFC5424Message struct {
Priority int
Facility int
Severity int
Time *time.Time
HostName string
AppName string
ProcID string
Message string
}
var (
ErrNoPriority = errors.New("message does not have a priority field")
ErrPriorityNotInt = errors.New("the priority field is not an integer")
ErrEmptyMessage = errors.New("empty syslog message")
ErrBadTimestamp = errors.New("invalid timestamp format")
)
func p3164(m []byte) (*RFC5424Message, error) {
m = bytes.TrimSpace(m)
smsg := &RFC5424Message{}
if !bytes.HasPrefix(m, []byte("<")) {
return nil, ErrNoPriority
}
priEnd := bytes.Index(m, []byte(">"))
if priEnd <= 1 {
return nil, ErrNoPriority
}
priStr := m[1:priEnd]
priNum, err := strconv.Atoi(string(priStr))
if err != nil {
return nil, ErrPriorityNotInt
}
smsg.Priority = priNum
smsg.Facility = priNum / 8
smsg.Severity = priNum % 8
if len(m) <= (priEnd + 1) {
return nil, ErrEmptyMessage
}
m = bytes.TrimSpace(m[priEnd+1:])
if len(m) == 0 {
return nil, ErrEmptyMessage
}
s := bytes.Fields(m)
if m[0] >= byte('0') && m[0] <= byte('9') {
// RFC3339
s0 := string(s[0])
t1, e := time.Parse(time.RFC3339Nano, s0)
if e != nil {
t2, e := time.Parse(time.RFC3339, s0)
if e != nil {
return nil, ErrBadTimestamp
}
smsg.Time = &t2
} else {
smsg.Time = &t1
}
if len(s) == 1 {
return nil, ErrEmptyMessage
}
s = s[1:]
} else {
// old unix timestamp
if len(s) < 3 {
return nil, ErrBadTimestamp
}
ts := string(bytes.Join(s[0:3], space))
t, e := time.ParseInLocation(time.Stamp, ts, time.Local)
if e != nil {
return nil, ErrBadTimestamp
}
t = t.AddDate(time.Now().Year(), 0, 0)
smsg.Time = &t
if len(s) == 3 {
return smsg, nil
}
s = s[3:]
}
if len(s) == 1 {
return nil, ErrEmptyMessage
}
if len(s) == 2 {
// we either have HOSTNAME/MESSAGE or TAG/MESSAGE or HOSTNAME/TAG
if bytes.Count(s[0], []byte(":")) == 7 || bytes.Count(s[0], []byte(".")) == 3 {
// looks like an IPv6/IPv4 address
smsg.HostName = string(s[0])
if bytes.ContainsAny(s[1], "[]:") {
smsg.AppName, smsg.ProcID = pair2str(parseTag(s[1]))
} else {
smsg.Message = string(s[1])
}
return smsg, nil
}
if bytes.ContainsAny(s[0], "[]:") {
smsg.AppName, smsg.ProcID = pair2str(parseTag(s[0]))
smsg.Message = string(s[1])
return smsg, nil
}
if bytes.ContainsAny(s[1], "[]:") {
smsg.HostName = string(s[0])
smsg.AppName, smsg.ProcID = pair2str(parseTag(s[0]))
return smsg, nil
}
smsg.AppName = string(s[0])
smsg.Message = string(s[1])
return smsg, nil
}
if bytes.ContainsAny(s[0], "[]:") || !isHostname(s[0]) {
// hostname is omitted
smsg.AppName, smsg.ProcID = pair2str(parseTag(s[0]))
smsg.Message = string(bytes.Join(s[1:], space))
return smsg, nil
}
smsg.HostName = string(s[0])
smsg.AppName, smsg.ProcID = pair2str(parseTag(s[1]))
smsg.Message = string(bytes.Join(s[2:], space))
return smsg, nil
}
func parseTag(tag []byte) (appname []byte, procid []byte) {
tag = bytes.Trim(tag, ":")
i := bytes.Index(tag, []byte("["))
if i >= 0 && len(tag) > (i+1) {
j := bytes.Index(tag, []byte("]"))
if j > i {
procid = tag[(i + 1):j]
} else {
procid = tag[(i + 1):]
}
if i > 0 {
appname = tag[0:i]
}
} else {
appname = tag
}
return
}
func isHostname(s []byte) bool {
for _, r := range string(s) {
if (!uni.IsLetter(r)) && (!uni.IsNumber(r)) && r != '.' && r != ':' && r != '-' && r != '_' {
return false
}
}
return true
}