parse_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. package rfc5424
  2. import (
  3. "testing"
  4. "time"
  5. )
  6. func TestPri(t *testing.T) {
  7. tests := []struct {
  8. input string
  9. expected int
  10. expectedErr string
  11. }{
  12. {"<0>", 0, ""},
  13. {"<19>", 19, ""},
  14. {"<200>", 200, ""},
  15. {"<4999>", 0, "PRI must be up to 3 characters long"},
  16. {"<123", 0, "PRI must end with '>'"},
  17. {"123>", 0, "PRI must start with '<'"},
  18. {"<abc>", 0, "PRI must be a number"},
  19. }
  20. for _, test := range tests {
  21. test := test
  22. t.Run(test.input, func(t *testing.T) {
  23. r := &RFC5424{}
  24. r.buf = []byte(test.input)
  25. r.len = len(r.buf)
  26. err := r.parsePRI()
  27. if err != nil {
  28. if test.expectedErr != "" {
  29. if err.Error() != test.expectedErr {
  30. t.Errorf("expected error %s, got %s", test.expectedErr, err)
  31. }
  32. } else {
  33. t.Errorf("unexpected error: %s", err)
  34. }
  35. } else {
  36. if test.expectedErr != "" {
  37. t.Errorf("expected error %s, got no error", test.expectedErr)
  38. } else {
  39. if r.PRI != test.expected {
  40. t.Errorf("expected %d, got %d", test.expected, r.PRI)
  41. }
  42. }
  43. }
  44. })
  45. }
  46. }
  47. func TestHostname(t *testing.T) {
  48. tests := []struct {
  49. input string
  50. expected string
  51. expectedErr string
  52. strictHostname bool
  53. }{
  54. {"127.0.0.1", "127.0.0.1", "", false},
  55. {"::1", "::1", "", false},
  56. {"-", "", "", false},
  57. {"foo.-bar", "", "hostname is not valid", true},
  58. {"foo-.bar", "", "hostname is not valid", true},
  59. {"foo123.bar", "foo123.bar", "", true},
  60. {"a..", "", "hostname is not valid", true},
  61. {"foo.bar", "foo.bar", "", false},
  62. {"foo,bar", "foo,bar", "", false},
  63. {"foo,bar", "", "hostname is not valid", true},
  64. {".", ".", "", true},
  65. {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "", "hostname is not valid", true},
  66. {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", "hostname is not valid", true},
  67. {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bla", "", false},
  68. {"a.foo-", "", "hostname is not valid", true},
  69. }
  70. for _, test := range tests {
  71. test := test
  72. t.Run(test.input, func(t *testing.T) {
  73. opts := []RFC5424Option{}
  74. if test.strictHostname {
  75. opts = append(opts, WithStrictHostname())
  76. }
  77. r := NewRFC5424Parser(opts...)
  78. r.buf = []byte(test.input)
  79. r.len = len(r.buf)
  80. err := r.parseHostname()
  81. if err != nil {
  82. if test.expectedErr != "" {
  83. if err.Error() != test.expectedErr {
  84. t.Errorf("expected error %s, got %s", test.expectedErr, err)
  85. }
  86. } else {
  87. t.Errorf("unexpected error: %s", err)
  88. }
  89. } else {
  90. if test.expectedErr != "" {
  91. t.Errorf("expected error %s, got no error", test.expectedErr)
  92. } else {
  93. if r.Hostname != test.expected {
  94. t.Errorf("expected %s, got %s", test.expected, r.Hostname)
  95. }
  96. }
  97. }
  98. })
  99. }
  100. }
  101. func TestParse(t *testing.T) {
  102. type expected struct {
  103. Timestamp time.Time
  104. Hostname string
  105. Tag string
  106. PID string
  107. Message string
  108. PRI int
  109. MsgID string
  110. }
  111. tests := []struct {
  112. name string
  113. input string
  114. expected expected
  115. expectedErr string
  116. opts []RFC5424Option
  117. }{
  118. {
  119. "valid msg",
  120. `<13>1 2021-05-18T11:58:40.828081+02:42 mantis sshd 49340 - [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{
  121. Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
  122. Hostname: "mantis",
  123. Tag: "sshd",
  124. PID: "49340",
  125. MsgID: "",
  126. Message: "blabla",
  127. PRI: 13,
  128. }, "", []RFC5424Option{},
  129. },
  130. {
  131. "valid msg with msgid",
  132. `<13>1 2021-05-18T11:58:40.828081+02:42 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{
  133. Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
  134. Hostname: "mantis",
  135. Tag: "foobar",
  136. PID: "49340",
  137. MsgID: "123123",
  138. Message: "blabla",
  139. PRI: 13,
  140. }, "", []RFC5424Option{},
  141. },
  142. {
  143. "valid msg with repeating SD",
  144. `<13>1 2021-05-18T11:58:40.828081+02:42 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"][foo="bar][a] blabla`, expected{
  145. Timestamp: time.Date(2021, 5, 18, 11, 58, 40, 828081000, time.FixedZone("+0242", 9720)),
  146. Hostname: "mantis",
  147. Tag: "foobar",
  148. PID: "49340",
  149. MsgID: "123123",
  150. Message: "blabla",
  151. PRI: 13,
  152. }, "", []RFC5424Option{},
  153. },
  154. {
  155. "invalid SD",
  156. `<13>1 2021-05-18T11:58:40.828081+02:00 mantis foobar 49340 123123 [timeQuality asd`, expected{}, "structured data must end with ']'", []RFC5424Option{},
  157. },
  158. {
  159. "invalid version",
  160. `<13>42 2021-05-18T11:58:40.828081+02:00 mantis foobar 49340 123123 [timeQuality isSynced="0" tzKnown="1"] blabla`, expected{}, "version must be 1", []RFC5424Option{},
  161. },
  162. {
  163. "invalid message",
  164. `<13>1`, expected{}, "version must be followed by a space", []RFC5424Option{},
  165. },
  166. {
  167. "valid msg with empty fields",
  168. `<13>1 - foo - - - - blabla`, expected{
  169. Timestamp: time.Now().UTC().Round(0),
  170. Hostname: "foo",
  171. PRI: 13,
  172. Message: "blabla",
  173. }, "", []RFC5424Option{},
  174. },
  175. {
  176. "valid msg with empty fields",
  177. `<13>1 - - - - - - blabla`, expected{
  178. Timestamp: time.Now().UTC().Round(0),
  179. PRI: 13,
  180. Message: "blabla",
  181. }, "", []RFC5424Option{},
  182. },
  183. {
  184. "valid msg with escaped SD",
  185. `<13>1 2022-05-24T10:57:39Z testhostname unknown - sn="msgid" [foo="\]" bar="a\""][a b="[\]" c] testmessage`,
  186. expected{
  187. PRI: 13,
  188. Timestamp: time.Date(2022, 5, 24, 10, 57, 39, 0, time.UTC),
  189. Tag: "unknown",
  190. Hostname: "testhostname",
  191. MsgID: `sn="msgid"`,
  192. Message: `testmessage`,
  193. }, "", []RFC5424Option{},
  194. },
  195. {
  196. "valid complex msg",
  197. `<13>1 2022-05-24T10:57:39Z myhostname unknown - sn="msgid" [all@0 request="/dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js" src_ip_geo_country="DE" MONTH="May" COMMONAPACHELOG="1.1.1.1 - - [24/May/2022:10:57:37 +0200\] \"GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0\" 304 0" auth="-" HOUR="10" gl2_remote_ip="172.31.32.142" ident="-" gl2_remote_port="43375" BASE10NUM="[2.0, 304, 0\]" pid="-1" program="nginx" gl2_source_input="623ed3440183476d61cff974" INT="+0200" is_private_ip="false" YEAR="2022" src_ip_geo_city="Achern" clientip="1.1.1.1" USERNAME="-" src_ip_geo_location="48.6306,8.0743" gl2_source_node="8620c2bb-dbb7-4535-b1ce-83df223acd8d" MINUTE="57" timestamp="2022-05-24T08:57:37.000Z" src_ip_asn="3320" level="5" IP="1.1.1.1" IPV4="1.1.1.1" verb="GET" gl2_message_id="01G3TMJFAMFS4H60QSF7M029R0" TIME="10:57:37" USER="-" src_ip_asn_owner="Deutsche Telekom AG" response="304" bytes="0" SECOND="37" httpversion="2.0" _id="906ce155-db3f-11ec-b25f-0a189ba2c64e" facility="user" MONTHDAY="24"] source: sn="www.foobar.com" | message: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 "https://www.foobar.com/sw.js" "Mozilla/5.0 (Linux; Android 9; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Mobile Safari/537.36" "-" "www.foobar.com" sn="www.foobar.com" rt=0.000 ua="-" us="-" ut="-" ul="-" cs=HIT { request: /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js | src_ip_geo_country: DE | MONTH: May | COMMONAPACHELOG: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 | auth: - | HOUR: 10 | gl2_remote_ip: 172.31.32.142 | ident: - | gl2_remote_port: 43375 | BASE10NUM: [2.0, 304, 0] | pid: -1 | program: nginx | gl2_source_input: 623ed3440183476d61cff974 | INT: +0200 | is_private_ip: false | YEAR: 2022 | src_ip_geo_city: Achern | clientip: 1.1.1.1 | USERNAME:`,
  198. expected{
  199. Timestamp: time.Date(2022, 5, 24, 10, 57, 39, 0, time.UTC),
  200. Hostname: "myhostname",
  201. Tag: "unknown",
  202. PRI: 13,
  203. MsgID: `sn="msgid"`,
  204. Message: `source: sn="www.foobar.com" | message: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 "https://www.foobar.com/sw.js" "Mozilla/5.0 (Linux; Android 9; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Mobile Safari/537.36" "-" "www.foobar.com" sn="www.foobar.com" rt=0.000 ua="-" us="-" ut="-" ul="-" cs=HIT { request: /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js | src_ip_geo_country: DE | MONTH: May | COMMONAPACHELOG: 1.1.1.1 - - [24/May/2022:10:57:37 +0200] "GET /dist/precache-manifest.58b57debe6bc4f96698da0dc314461e9.js HTTP/2.0" 304 0 | auth: - | HOUR: 10 | gl2_remote_ip: 172.31.32.142 | ident: - | gl2_remote_port: 43375 | BASE10NUM: [2.0, 304, 0] | pid: -1 | program: nginx | gl2_source_input: 623ed3440183476d61cff974 | INT: +0200 | is_private_ip: false | YEAR: 2022 | src_ip_geo_city: Achern | clientip: 1.1.1.1 | USERNAME:`,
  205. }, "", []RFC5424Option{},
  206. },
  207. {
  208. "partial message",
  209. `<13>1 2022-05-24T10:57:39Z foo bar -`,
  210. expected{},
  211. "EOL after ProcID",
  212. []RFC5424Option{},
  213. },
  214. {
  215. "partial message",
  216. `<13>1 2022-05-24T10:57:39Z foo bar `,
  217. expected{},
  218. "EOL after appname",
  219. []RFC5424Option{},
  220. },
  221. }
  222. for _, test := range tests {
  223. test := test
  224. t.Run(test.name, func(t *testing.T) {
  225. r := NewRFC5424Parser(test.opts...)
  226. err := r.Parse([]byte(test.input))
  227. if err != nil {
  228. if test.expectedErr != "" {
  229. if err.Error() != test.expectedErr {
  230. t.Errorf("expected error '%s', got '%s'", test.expectedErr, err)
  231. }
  232. } else {
  233. t.Errorf("unexpected error: '%s'", err)
  234. }
  235. } else {
  236. if test.expectedErr != "" {
  237. t.Errorf("expected error '%s', got no error", test.expectedErr)
  238. } else {
  239. if r.Timestamp.Round(time.Second).String() != test.expected.Timestamp.Round(time.Second).String() {
  240. t.Errorf("expected timestamp '%s', got '%s'", test.expected.Timestamp, r.Timestamp)
  241. }
  242. if r.Hostname != test.expected.Hostname {
  243. t.Errorf("expected hostname '%s', got '%s'", test.expected.Hostname, r.Hostname)
  244. }
  245. if r.Tag != test.expected.Tag {
  246. t.Errorf("expected tag '%s', got '%s'", test.expected.Tag, r.Tag)
  247. }
  248. if r.PID != test.expected.PID {
  249. t.Errorf("expected pid '%s', got '%s'", test.expected.PID, r.PID)
  250. }
  251. if r.Message != test.expected.Message {
  252. t.Errorf("expected message '%s', got '%s'", test.expected.Message, r.Message)
  253. }
  254. if r.PRI != test.expected.PRI {
  255. t.Errorf("expected pri '%d', got '%d'", test.expected.PRI, r.PRI)
  256. }
  257. if r.MsgID != test.expected.MsgID {
  258. t.Errorf("expected msgid '%s', got '%s'", test.expected.MsgID, r.MsgID)
  259. }
  260. }
  261. }
  262. })
  263. }
  264. }