1 
2 use chrono::prelude::*;
3 use syslog_loose::{parse_message,
4                    parse_message_with_year,
5                    IncompleteDate,
6                    Message,
7                    ProcId,
8                    Protocol,
9                    StructuredElement,
10                    SyslogFacility,
11                    SyslogSeverity};
12 
with_year((month, _date, _hour, _min, _sec): IncompleteDate) -> i3213 fn with_year((month, _date, _hour, _min, _sec): IncompleteDate) -> i32 {
14     if month == 12 {
15         2019
16     } else {
17         2020
18     }
19 }
20 
21 #[test]
parse_nginx()22 fn parse_nginx() {
23     // The nginx logs in 3164.
24     let msg = "<190>Dec 28 16:49:07 plertrood-thinkpad-x220 nginx: 127.0.0.1 - - [28/Dec/2019:16:49:07 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0\"";
25 
26     assert_eq!(
27         parse_message_with_year(msg, with_year),
28         Message {
29 
30             facility: Some(SyslogFacility::LOG_LOCAL7),
31             severity: Some(SyslogSeverity::SEV_INFO),
32             timestamp: Some(FixedOffset::west(0).ymd(2019, 12, 28).and_hms(16, 49, 07)),
33             hostname: Some("plertrood-thinkpad-x220"),
34             appname: Some("nginx"),
35             procid: None,
36             msgid: None,
37             protocol: Protocol::RFC3164,
38             structured_data: vec![],
39             msg: "127.0.0.1 - - [28/Dec/2019:16:49:07 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0\"",
40         }
41     );
42 }
43 
44 #[test]
parse_rsyslog()45 fn parse_rsyslog() {
46     // rsyslog sends messages in 3164 with some structured data.
47     let msg = "<46>Jan  5 15:33:03 plertrood-ThinkPad-X220 rsyslogd:  [origin software=\"rsyslogd\" swVersion=\"8.32.0\" x-pid=\"20506\" x-info=\"http://www.rsyslog.com\"] start";
48 
49     assert_eq!(
50         parse_message_with_year(msg, with_year),
51         Message {
52             facility: Some(SyslogFacility::LOG_SYSLOG),
53             severity: Some(SyslogSeverity::SEV_INFO),
54             timestamp: Some(
55                 FixedOffset::west(0)
56                     .ymd(2020, 1, 5)
57                     .and_hms_milli(15, 33, 3, 0)
58             ),
59             hostname: Some("plertrood-ThinkPad-X220"),
60             appname: Some("rsyslogd"),
61             procid: None,
62             msgid: None,
63             protocol: Protocol::RFC3164,
64             structured_data: vec![StructuredElement {
65                 id: "origin",
66                 params: vec![
67                     ("software", "rsyslogd"),
68                     ("swVersion", "8.32.0"),
69                     ("x-pid", "20506"),
70                     ("x-info", "http://www.rsyslog.com"),
71                 ]
72             }],
73             msg: "start",
74         }
75     );
76 }
77 
78 #[test]
parse_haproxy()79 fn parse_haproxy() {
80     // haproxy doesnt include the hostname.
81     let msg = "<133>Jan 13 16:33:35 haproxy[73411]: Proxy sticky-servers started.";
82     assert_eq!(
83         parse_message_with_year(msg, with_year),
84         Message {
85             facility: Some(SyslogFacility::LOG_LOCAL0),
86             severity: Some(SyslogSeverity::SEV_NOTICE),
87             timestamp: Some(
88                 FixedOffset::west(0)
89                     .ymd(2020, 1, 13)
90                     .and_hms_milli(16, 33, 35, 0)
91             ),
92             hostname: None,
93             appname: Some("haproxy"),
94             procid: Some(ProcId::PID(73411)),
95             msgid: None,
96             protocol: Protocol::RFC3164,
97             structured_data: vec![],
98             msg: "Proxy sticky-servers started.",
99         }
100     );
101 }
102 
103 #[test]
parse_5424_no_structured_data()104 fn parse_5424_no_structured_data() {
105     let msg = "<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on /dev/pts/8";
106 
107     assert_eq!(
108         parse_message(msg),
109         Message {
110             facility: Some(SyslogFacility::LOG_AUTH),
111             severity: Some(SyslogSeverity::SEV_CRIT),
112             timestamp: Some(
113                 FixedOffset::west(0)
114                     .ymd(2003, 10, 11)
115                     .and_hms_milli(22, 14, 15, 3)
116             ),
117             hostname: Some("mymachine.example.com"),
118             appname: Some("su"),
119             procid: None,
120             msgid: Some("ID47"),
121             protocol: Protocol::RFC5424(1),
122             structured_data: vec![],
123             msg: "BOM'su root' failed for lonvick on /dev/pts/8",
124         }
125     );
126 }
127 
128 #[test]
parse_5424_structured_data()129 fn parse_5424_structured_data() {
130     let msg = "<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut=\"3\" eventSource=\"Application\" eventID=\"1011\"] BOMAn application event log entry...";
131 
132     assert_eq!(
133         parse_message(msg),
134         Message {
135             facility: Some(SyslogFacility::LOG_LOCAL4),
136             severity: Some(SyslogSeverity::SEV_NOTICE),
137             timestamp: Some(
138                 FixedOffset::west(0)
139                     .ymd(2003, 10, 11)
140                     .and_hms_milli(22, 14, 15, 3)
141             ),
142             hostname: Some("mymachine.example.com"),
143             appname: Some("evntslog"),
144             procid: None,
145             msgid: Some("ID47"),
146             protocol: Protocol::RFC5424(1),
147             structured_data: vec![StructuredElement {
148                 id: "exampleSDID@32473",
149                 params: vec![
150                     ("iut", "3"),
151                     ("eventSource", "Application"),
152                     ("eventID", "1011")
153                 ]
154             },],
155             msg: "BOMAn application event log entry...",
156         }
157     );
158 }
159 
160 #[test]
parse_5424_multiple_structured_data()161 fn parse_5424_multiple_structured_data() {
162     let msg = "<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut=\"3\" eventSource= \"Application\" eventID=\"1011\"][examplePriority@32473 class=\"high\"] BOMAn application event log entry...";
163 
164     assert_eq!(
165         parse_message(msg),
166         Message {
167             facility: Some(SyslogFacility::LOG_LOCAL4),
168             severity: Some(SyslogSeverity::SEV_NOTICE),
169             timestamp: Some(
170                 FixedOffset::west(0)
171                     .ymd(2003, 10, 11)
172                     .and_hms_milli(22, 14, 15, 3)
173             ),
174             hostname: Some("mymachine.example.com"),
175             appname: Some("evntslog"),
176             procid: None,
177             msgid: Some("ID47"),
178             protocol: Protocol::RFC5424(1),
179             structured_data: vec![
180                 StructuredElement {
181                     id: "exampleSDID@32473",
182                     params: vec![
183                         ("iut", "3"),
184                         ("eventSource", "Application"),
185                         ("eventID", "1011")
186                     ]
187                 },
188                 StructuredElement {
189                     id: "examplePriority@32473",
190                     params: vec![("class", "high"),]
191                 }
192             ],
193             msg: "BOMAn application event log entry...",
194         }
195     );
196 }
197 
198 #[test]
parse_3164_invalid_structured_data()199 fn parse_3164_invalid_structured_data() {
200     // Can 3164 parse ok when there is something looking similar to structured data - but not quite.
201     // Remove the id from the rsyslog messages structured data. This should now go into the msg.
202     let msg = "<46>Jan  5 15:33:03 plertrood-ThinkPad-X220 rsyslogd:  [software=\"rsyslogd\" swVersion=\"8.32.0\" x-pid=\"20506\" x-info=\"http://www.rsyslog.com\"] start";
203 
204     assert_eq!(parse_message_with_year(msg, with_year),
205                Message {
206                    facility: Some(SyslogFacility::LOG_SYSLOG),
207                    severity: Some(SyslogSeverity::SEV_INFO),
208                    timestamp: Some(
209                        FixedOffset::west(0)
210                            .ymd(2020, 1, 5)
211                            .and_hms_milli(15, 33, 3, 0)
212                    ),
213                    hostname: Some("plertrood-ThinkPad-X220"),
214                    appname: Some("rsyslogd"),
215                    procid: None,
216                    msgid: None,
217                    protocol: Protocol::RFC3164,
218                    structured_data: vec![],
219                    msg: "start",
220                });
221 }
222 
223 #[test]
parse_european_chars()224 fn parse_european_chars() {
225     let msg = "<46>Jan 5 10:01:00 Übergröße außerplanmäßig größenordnungsmäßig";
226 
227     assert_eq!(
228         parse_message_with_year(msg, with_year),
229         Message {
230             facility: Some(SyslogFacility::LOG_SYSLOG),
231             severity: Some(SyslogSeverity::SEV_INFO),
232             timestamp: Some(
233                 FixedOffset::west(0)
234                     .ymd(2020, 1, 5)
235                     .and_hms_milli(10, 1, 0, 0)
236             ),
237             hostname: Some("Übergröße"),
238             appname: Some("außerplanmäßig"),
239             procid: None,
240             msgid: None,
241             protocol: Protocol::RFC3164,
242             structured_data: vec![],
243             msg: "größenordnungsmäßig",
244         }
245     );
246 }
247 
248 #[test]
parse_invalid_message()249 fn parse_invalid_message() {
250     let msg = "complete and utter gobbledegook";
251 
252     assert_eq!(
253         parse_message_with_year(msg, with_year),
254         Message {
255             facility: None,
256             severity: None,
257             timestamp: None,
258             hostname: None,
259             appname: None,
260             procid: None,
261             msgid: None,
262             protocol: Protocol::RFC3164,
263             structured_data: vec![],
264             msg: "complete and utter gobbledegook",
265         }
266     );
267 }
268 
269 #[test]
parse_blank_msg()270 fn parse_blank_msg() {
271     let ook = Message {
272         facility: Some(SyslogFacility::LOG_CRON),
273         severity: Some(SyslogSeverity::SEV_ERR),
274         timestamp: Some(
275             FixedOffset::west(0)
276                 .ymd(1969, 12, 3)
277                 .and_hms_milli(23, 58, 58, 0),
278         ),
279         hostname: None,
280         appname: None,
281         procid: None,
282         msgid: None,
283         protocol: Protocol::RFC5424(1),
284         structured_data: vec![],
285         msg: "",
286     };
287 
288     println!("{}", ook);
289     let msg = format!("{}", ook);
290 
291     assert_eq!(
292         parse_message(&msg),
293         Message {
294             facility: Some(SyslogFacility::LOG_CRON),
295             severity: Some(SyslogSeverity::SEV_ERR),
296             timestamp: Some(
297                 FixedOffset::west(0)
298                     .ymd(1969, 12, 3)
299                     .and_hms_milli(23, 58, 58, 0),
300             ),
301             hostname: None,
302             appname: None,
303             procid: None,
304             msgid: None,
305             protocol: Protocol::RFC3164,
306             structured_data: vec![],
307             msg: "",
308         }
309     );
310 }
311 
312 
313 
314 /*
315 
316 The following tests have been taken from Vector (vector.dev)
317 https://github.com/timberio/vector/blob/fff92728c9490824ff9d0ae76669adc901bb5499/src/sources/syslog.rs
318 
319 */
320 
321 #[test]
syslog_ng_network_syslog_protocol()322 fn syslog_ng_network_syslog_protocol() {
323     let msg = "i am foobar";
324     let raw = format!(
325         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {}{} {}"#,
326         r#"[meta sequenceId="1" sysUpTime="37" language="EN"]"#,
327         r#"[origin ip="192.168.0.1" software="test"]"#,
328         msg
329     );
330 
331     assert_eq!(
332         parse_message(&raw),
333         Message {
334             facility: Some(SyslogFacility::LOG_USER),
335             severity: Some(SyslogSeverity::SEV_NOTICE),
336             timestamp: Some(
337                 FixedOffset::west(0)
338                     .ymd(2019, 02, 13)
339                     .and_hms_milli(19, 48, 34, 0)
340             ),
341             hostname: Some("74794bfb6795"),
342             appname: Some("root"),
343             procid: Some(ProcId::PID(8449)),
344             msgid: None,
345             protocol: Protocol::RFC5424(1),
346             structured_data: vec![
347                 StructuredElement {
348                     id: "meta",
349                     params: vec![
350                         ("sequenceId", "1"),
351                         ("sysUpTime", "37"),
352                         ("language", "EN")
353                     ]
354                 },
355                 StructuredElement {
356                     id: "origin",
357                     params: vec![("ip", "192.168.0.1"),
358                                  ("software", "test"),
359                     ]
360                 }
361             ],
362             msg: "i am foobar",
363         }
364     )
365 }
366 
367 #[test]
handles_incorrect_sd_element()368 fn handles_incorrect_sd_element() {
369     let msg = format!(
370         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
371         r#"[incorrect x]"#
372     );
373 
374     let should = Message {
375         facility: Some(SyslogFacility::LOG_USER),
376         severity: Some(SyslogSeverity::SEV_NOTICE),
377         timestamp: Some(
378             FixedOffset::west(0)
379                 .ymd(2019, 02, 13)
380                 .and_hms_milli(19, 48, 34, 0)
381         ),
382         hostname: Some("74794bfb6795"),
383         appname: Some("root"),
384         procid: Some(ProcId::PID(8449)),
385         msgid: None,
386         protocol: Protocol::RFC5424(1),
387         structured_data: vec![],
388         msg: "qwerty",
389     };
390 
391     assert_eq!(
392         parse_message(&msg),
393         should
394     );
395 
396     let msg = format!(
397         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
398         r#"[incorrect x=]"#
399     );
400 
401     assert_eq!(
402         parse_message(&msg),
403         should
404     );
405 }
406 
407 
408 #[test]
handles_empty_sd_element()409 fn handles_empty_sd_element() {
410     let msg = format!(
411         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
412         r#"[empty]"#
413     );
414 
415     assert_eq!(
416         parse_message(&msg),
417         Message {
418             facility: Some(SyslogFacility::LOG_USER),
419             severity: Some(SyslogSeverity::SEV_NOTICE),
420             timestamp: Some(
421                 FixedOffset::west(0)
422                     .ymd(2019, 02, 13)
423                     .and_hms_milli(19, 48, 34, 0)
424             ),
425             hostname: Some("74794bfb6795"),
426             appname: Some("root"),
427             procid: Some(ProcId::PID(8449)),
428             msgid: None,
429             protocol: Protocol::RFC5424(1),
430             structured_data: vec![
431                 StructuredElement {
432                     id: "empty",
433                     params: vec![]
434                 }
435             ],
436             msg: "qwerty",
437         });
438 
439     let msg = format!(
440         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
441         r#"[non_empty x="1"][empty]"#
442     );
443 
444     assert_eq!(
445         parse_message(&msg),
446         Message {
447             facility: Some(SyslogFacility::LOG_USER),
448             severity: Some(SyslogSeverity::SEV_NOTICE),
449             timestamp: Some(
450                 FixedOffset::west(0)
451                     .ymd(2019, 02, 13)
452                     .and_hms_milli(19, 48, 34, 0)
453             ),
454             hostname: Some("74794bfb6795"),
455             appname: Some("root"),
456             procid: Some(ProcId::PID(8449)),
457             msgid: None,
458             protocol: Protocol::RFC5424(1),
459             structured_data: vec![
460                 StructuredElement {
461                     id: "non_empty",
462                     params: vec![("x", "1")]
463                 },
464                 StructuredElement {
465                     id: "empty",
466                     params: vec![]
467                 },
468             ],
469             msg: "qwerty",
470         });
471 
472     let msg = format!(
473         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
474         r#"[empty][non_empty x="1"]"#
475     );
476 
477     assert_eq!(
478         parse_message(&msg),
479         Message {
480             facility: Some(SyslogFacility::LOG_USER),
481             severity: Some(SyslogSeverity::SEV_NOTICE),
482             timestamp: Some(
483                 FixedOffset::west(0)
484                     .ymd(2019, 02, 13)
485                     .and_hms_milli(19, 48, 34, 0)
486             ),
487             hostname: Some("74794bfb6795"),
488             appname: Some("root"),
489             procid: Some(ProcId::PID(8449)),
490             msgid: None,
491             protocol: Protocol::RFC5424(1),
492             structured_data: vec![
493                 StructuredElement {
494                     id: "empty",
495                     params: vec![]
496                 },
497                 StructuredElement {
498                     id: "non_empty",
499                     params: vec![("x", "1")]
500                 },
501             ],
502             msg: "qwerty",
503         });
504 
505     let msg = format!(
506         r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
507         r#"[empty not_really="testing the test"]"#
508     );
509 
510     assert_eq!(
511         parse_message(&msg),
512         Message {
513             facility: Some(SyslogFacility::LOG_USER),
514             severity: Some(SyslogSeverity::SEV_NOTICE),
515             timestamp: Some(
516                 FixedOffset::west(0)
517                     .ymd(2019, 02, 13)
518                     .and_hms_milli(19, 48, 34, 0)
519             ),
520             hostname: Some("74794bfb6795"),
521             appname: Some("root"),
522             procid: Some(ProcId::PID(8449)),
523             msgid: None,
524             protocol: Protocol::RFC5424(1),
525             structured_data: vec![
526                 StructuredElement {
527                     id: "empty",
528                     params: vec![("not_really", "testing the test")]
529                 },
530             ],
531             msg: "qwerty",
532         });
533 
534 }
535 
536 #[test]
handles_weird_whitespace()537 fn handles_weird_whitespace() {
538     // this should also match rsyslog omfwd with template=RSYSLOG_SyslogProtocol23Format
539     let raw = r#"
540             <13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - [meta sequenceId="1"] i am foobar
541             "#;
542     let cleaned = r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - [meta sequenceId="1"] i am foobar"#;
543 
544     assert_eq!(
545         parse_message(&raw),
546         parse_message(&cleaned)
547     );
548 }
549 
550 
551 #[test]
syslog_ng_default_network()552 fn syslog_ng_default_network() {
553     let raw = r#"<13>Feb 13 20:07:26 74794bfb6795 root[8539]: i am foobar"#;
554 
555     assert_eq!(
556         parse_message_with_year(&raw, with_year),
557         Message {
558             facility: Some(SyslogFacility::LOG_USER),
559             severity: Some(SyslogSeverity::SEV_NOTICE),
560             timestamp: Some(
561                 FixedOffset::west(0)
562                     .ymd(2020, 02, 13)
563                     .and_hms(20, 07, 26)
564             ),
565             hostname: Some("74794bfb6795"),
566             appname: Some("root"),
567             procid: Some(ProcId::PID(8539)),
568             msgid: None,
569             protocol: Protocol::RFC3164,
570             structured_data: vec![],
571             msg: "i am foobar",
572         }
573     );
574 }
575 
576 #[test]
rsyslog_omfwd_tcp_default()577 fn rsyslog_omfwd_tcp_default() {
578     let raw = r#"<190>Feb 13 21:31:56 74794bfb6795 liblogging-stdlog:  [origin software="rsyslogd" swVersion="8.24.0" x-pid="8979" x-info="http://www.rsyslog.com"] start"#;
579 
580     assert_eq!(
581         parse_message_with_year(&raw, with_year),
582         Message {
583             facility: Some(SyslogFacility::LOG_LOCAL7),
584             severity: Some(SyslogSeverity::SEV_INFO),
585             timestamp: Some(
586                 FixedOffset::west(0)
587                     .ymd(2020, 02, 13)
588                     .and_hms(21, 31, 56)
589             ),
590             hostname: Some("74794bfb6795"),
591             appname: Some("liblogging-stdlog"),
592             procid: None,
593             msgid: None,
594             protocol: Protocol::RFC3164,
595             structured_data: vec![
596                 StructuredElement {
597                     id: "origin",
598                     params: vec![("software", "rsyslogd"),
599                                  ("swVersion", "8.24.0"),
600                                  ("x-pid", "8979"),
601                                  ("x-info", "http://www.rsyslog.com")]
602                 }],
603             msg: "start",
604         }
605     );
606 }
607 
608 #[test]
rsyslog_omfwd_tcp_forward_format()609 fn rsyslog_omfwd_tcp_forward_format() {
610     let raw = r#"<190>2019-02-13T21:53:30.605850+00:00 74794bfb6795 liblogging-stdlog:  [origin software="rsyslogd" swVersion="8.24.0" x-pid="9043" x-info="http://www.rsyslog.com"] start"#;
611 
612     assert_eq!(
613         parse_message_with_year(&raw, with_year),
614         Message {
615             facility: Some(SyslogFacility::LOG_LOCAL7),
616             severity: Some(SyslogSeverity::SEV_INFO),
617             timestamp: Some(
618                 FixedOffset::west(0)
619                     .ymd(2019, 02, 13)
620                     .and_hms_micro(21, 53, 30, 605_850)
621             ),
622             hostname: Some("74794bfb6795"),
623             appname: Some("liblogging-stdlog"),
624             procid: None,
625             msgid: None,
626             protocol: Protocol::RFC3164,
627             structured_data: vec![
628                 StructuredElement {
629                     id: "origin",
630                     params: vec![("software", "rsyslogd"),
631                                  ("swVersion", "8.24.0"),
632                                  ("x-pid", "8979"),
633                                  ("x-info", "http://www.rsyslog.com")]
634                 }],
635             msg: "start",
636         }
637     );
638 }
639 
640 #[test]
logical_system_juniper_routers()641 fn logical_system_juniper_routers() {
642     let raw = r#"<28>1 2020-05-22T14:59:09.250-03:00 OX-XXX-MX204 OX-XXX-CONTEUDO:rpd 6589 - - bgp_listen_accept: %DAEMON-4: Connection attempt from unconfigured neighbor: 2001:XXX::219:166+57284"#;
643 
644     assert_eq!(
645         parse_message_with_year(&raw, with_year),
646         Message {
647             facility: Some(SyslogFacility::LOG_DAEMON),
648             severity: Some(SyslogSeverity::SEV_WARNING),
649             timestamp: Some(
650                 FixedOffset::west(1800 * 6)
651                     .ymd(2020, 05, 22)
652                     .and_hms_micro(14, 59, 09, 250000)
653             ),
654             hostname: Some("OX-XXX-MX204"),
655             appname: Some("OX-XXX-CONTEUDO:rpd"),
656             procid: Some(ProcId::PID(6589)),
657             msgid: None,
658             protocol: Protocol::RFC5424(1),
659             structured_data: vec![],
660             msg: "bgp_listen_accept: %DAEMON-4: Connection attempt from unconfigured neighbor: 2001:XXX::219:166+57284",
661         }
662     );
663 }
664