1use nom;
2use time;
3
4use std::str::FromStr;
5
6use nom::{digit, rest_s, space};
7
8fn parse_month(s: &str) -> Result<i32, nom::simple_errors::Err> {
9 match s {
10 "Jan" => Ok(1),
11 "Feb" => Ok(2),
12 "Mar" => Ok(3),
13 "Apr" => Ok(4),
14 "May" => Ok(5),
15 "Jun" => Ok(6),
16 "Jul" => Ok(7),
17 "Aug" => Ok(8),
18 "Sep" => Ok(9),
19 "Oct" => Ok(10),
20 "Nov" => Ok(11),
21 "Dec" => Ok(12),
22 _ => Err(error_position!(
23 nom::ErrorKind::OneOf,
24 "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec"
25 )),
26 }
27}
28
29named!(ts<&str,time::Tm>,
30 do_parse! (
31 month: map_res!(take!(3), parse_month) >>
32 space >>
33 day: map_res!(digit, FromStr::from_str) >>
34 space >>
35 hour: map_res!(digit, FromStr::from_str) >>
36 tag_s!(":") >>
37 minute: digit >>
38 tag_s!(":") >>
39 second: digit >>
40 ({
41 let mut tm = time::empty_tm();
42 tm.tm_mon = month;
43 tm.tm_mday = day;
44 tm.tm_hour = hour;
45 tm.tm_year = 2017 - 1900;
46 tm
47 })
48 )
49 );
50
51#[derive(Debug)]
52pub struct Syslog3164Message<'a> {
53 pub pri: &'a str,
54 pub ts: time::Tm,
55 pub host: &'a str,
56 pub tag: Option<(&'a str, Option<&'a str>)>,
57 pub msg: &'a str,
58}
59
60fn tag_delim(ch: char) -> bool {
61 ch == ' ' || ch == ':'
62}
63
64named!(parse_tag<&str, (&str,Option<&str>)>,
65 alt!(
66 do_parse!(
67 tag: take_until_s!("[") >>
68 tag_s!("[") >>
69 pid: take_until_s!("]") >>
70 tag_s!("]:") >>
71 (
72 (tag,Some(pid))
73 )
74 ) |
75 do_parse!(
76 tag: take_till_s!(tag_delim) >>
77 tag_s!(":") >>
78 ({
79 (tag,None)
80 })
81 )
82 )
83);
84
85#[test]
86fn parse_cisco_variation_1() {
87 use nom::IResult;
88 let msg1 = r##"<189>Dec 26 23:33:18 10.4.104.208 3890188: Dec 26 2017 23:33:17.792 UTC: %ADJ-5-RESOLVE_REQ_FAIL: Adj resolve request failed for 192.168.57.182 on Vlan55"##;
89 let res: IResult<&str, Syslog3164Message> = parse_syslog(msg1);
90 assert!(res.is_done());
91
92 let (_leftover, parsed) = res.unwrap();
93 assert_eq!(
94 parsed.msg,
95 "%ADJ-5-RESOLVE_REQ_FAIL: Adj resolve request failed for 192.168.57.182 on Vlan55"
96 );
97 assert_eq!(parsed.tag, Some(("3890188", None)));
98 assert_eq!(parsed.host, "10.4.104.208");
99 assert_eq!(parsed.ts.to_utc().to_timespec().sec, 1517007600)
100}
101
102named!(better_ts<&str, time::Tm>,
104 do_parse!(
105 month: map_res!(take_s!(3), parse_month) >>
106 tag_s!(" ") >>
107 day: map_res!(take_s!(2), FromStr::from_str) >>
108 tag_s!(" ") >>
109 year: map_res!(take_s!(4), FromStr::from_str) >>
110 tag_s!(" ") >>
111 hour: map_res!(take_s!(2), FromStr::from_str) >>
112 tag_s!(":") >>
113 minute: map_res!(take_s!(2), FromStr::from_str) >>
114 tag_s!(":") >>
115 seconds: map_res!(take_s!(2), FromStr::from_str) >>
116 tag_s!(".") >>
117 millis: map_res!(take_s!(3), FromStr::from_str) >>
118 tag_s!(" UTC: ") >>
119 ({
120 let mut tm = time::empty_tm();
121
122 tm.tm_year = year;
123 tm.tm_year = tm.tm_year - 1900;
124 tm.tm_mon = month;
125 tm.tm_mday = day;
126 tm.tm_hour = hour;
127 tm.tm_min = minute;
128 tm.tm_sec = seconds;
129 tm.tm_nsec = millis;
130 tm.tm_nsec = tm.tm_nsec * 1000000;
131
132 tm
133 })
134 )
135);
136
137named!(pub parse_syslog<&str, Syslog3164Message>,
138 do_parse!(
139 tag_s!("<") >>
140 pri: digit >>
141 tag_s!(">") >>
142 ts: ts >>
143 space >>
144 host: take_until_s!(" ") >>
145 space >>
146 tag: opt!(parse_tag) >>
147 space >>
148 better_ts: opt!(better_ts) >>
149 msg: rest_s >>
150 (Syslog3164Message{pri: pri.into(),
151 ts: ts,
152 host: host.into(),
153 tag: tag,
154 msg: msg.into()
155 })
156 )
157 );
158
159#[test]
160fn parse_nx_win_evt() {
161 use nom::IResult;
162 let msg1 = r##"<14>Dec 13 17:45:02 SANTA-CLAUS-W764.blerg.com nxWinEvt[123]: {"EventTime":"🐌017-12-19 17:45:02","Hostname":"fake-hostname","Keywords":-9214364837600034816,"EventType":"AUDIT_SUCCESS","SeverityValue":2,"Severity":"INFO","EventID":4656,"SourceName":"Microsoft-Windows-Security-Auditing","ProviderGuid":"{54849625-5478-4994-A5BA-3E3B0328C30D}","Version":1,"Task":12804,"OpcodeValue":0,"RecordNumber":7613465324,"ProcessID":892,"ThreadID":908,"Channel":"Security","AccessReason":"-","AccessMask":"0x2","PrivilegeList":"-","RestrictedSidCount":"0","ProcessName":"C:\\Windows\\System32\\svchost.exe","EventReceivedTime":"2017-12-19 17:52:27","SourceModuleName":"eventlog","SourceModuleType":"im_msvistalog"}"##;
163 let res: IResult<&str, Syslog3164Message> = parse_syslog(msg1);
164 assert!(res.is_done());
165
166 let (_leftover, parsed) = res.unwrap();
167 assert_eq!(parsed.msg, &msg1[62..]);
168 assert_eq!(parsed.ts.to_utc().to_timespec().sec, 1515862800)
169}
170
171#[test]
172fn parse_nx_win_evt_pid_variation_1() {
173 use nom::IResult;
174 let msg1 = r##"<14>Dec 13 17:45:02 SANTA-CLAUS-W764.blerg.com nxWinEvt: {"EventTime":"2017-12-19 17:45:02","Hostname":"fake-hostname","Keywords":-9214364837600034816,"EventType":"AUDIT_SUCCESS","SeverityValue":2,"Severity":"INFO","EventID":4656,"SourceName":"Microsoft-Windows-Security-Auditing","ProviderGuid":"{54849625-5478-4994-A5BA-3E3B0328C30D}","Version":1,"Task":12804,"OpcodeValue":0,"RecordNumber":7613465324,"ProcessID":892,"ThreadID":908,"Channel":"Security","AccessReason":"-","AccessMask":"0x2","PrivilegeList":"-","RestrictedSidCount":"0","ProcessName":"C:\\Windows\\System32\\svchost.exe","EventReceivedTime":"2017-12-19 17:52:27","SourceModuleName":"eventlog","SourceModuleType":"im_msvistalog"}"##;
175 let res: IResult<&str, Syslog3164Message> = parse_syslog(msg1);
176 assert!(res.is_done());
177
178 let (_leftover, parsed) = res.unwrap();
179 assert_eq!(parsed.msg, &msg1[57..]);
180 assert_eq!(parsed.ts.to_utc().to_timespec().sec, 1515862800)
181}