diff --git a/src/chunks/firehose/activity.rs b/src/chunks/firehose/activity.rs index 5295bc9..03f2973 100755 --- a/src/chunks/firehose/activity.rs +++ b/src/chunks/firehose/activity.rs @@ -265,11 +265,11 @@ mod tests { assert_eq!(results.unknown_activity_id_3, 64435); assert_eq!(results.unknown_sentinal_3, 2147483648); assert_eq!(results.unknown_message_string_ref, 0); - assert_eq!(results.firehose_formatters.main_exe, false); - assert_eq!(results.firehose_formatters.absolute, false); - assert_eq!(results.firehose_formatters.shared_cache, false); - assert_eq!(results.firehose_formatters.main_plugin, false); - assert_eq!(results.firehose_formatters.pc_style, false); + assert!(!results.firehose_formatters.main_exe); + assert!(!results.firehose_formatters.absolute); + assert!(!results.firehose_formatters.shared_cache); + assert!(!results.firehose_formatters.main_plugin); + assert!(!results.firehose_formatters.pc_style); assert_eq!(results.firehose_formatters.main_exe_alt_index, 0); assert_eq!(results.firehose_formatters.uuid_relative, ""); assert_eq!(results.unknown_pc_id, 303578944); diff --git a/src/decoders/bool.rs b/src/decoders/bool.rs index d27bb18..ca24012 100644 --- a/src/decoders/bool.rs +++ b/src/decoders/bool.rs @@ -22,9 +22,9 @@ pub(crate) fn lowercase_bool(bool_data: &str) -> String { } /// Return int value to bool -pub(crate) fn lowercase_int_bool(bool_data: &u8) -> String { - let false_bool = 0; - if bool_data == &false_bool { +pub(crate) fn lowercase_int_bool(bool_data: u8) -> String { + const FALSE_BOOL: u8 = 0; + if bool_data == FALSE_BOOL { return String::from("false"); } String::from("true") @@ -32,7 +32,7 @@ pub(crate) fn lowercase_int_bool(bool_data: &u8) -> String { #[cfg(test)] mod tests { - use crate::decoders::bool::{lowercase_bool, lowercase_int_bool, uppercase_bool}; + use super::*; #[test] fn test_uppercase_bool() { @@ -59,7 +59,7 @@ mod tests { #[test] fn test_lowercase_int_bool() { let test_data = 0; - let results = lowercase_int_bool(&test_data); + let results = lowercase_int_bool(test_data); assert_eq!(results, "false"); } } diff --git a/src/decoders/decoder.rs b/src/decoders/decoder.rs index 7004f1e..0717b42 100644 --- a/src/decoders/decoder.rs +++ b/src/decoders/decoder.rs @@ -23,6 +23,7 @@ use crate::{ opendirectory::{errors, member_details, member_id_type, sid_details}, time::parse_time, uuid::parse_uuid, + DecoderError, }, }; @@ -30,101 +31,116 @@ use crate::{ pub(crate) fn check_objects( format_string: &str, message_values: &[FirehoseItemInfo], - item_type: &u8, + item_type: u8, item_index: usize, ) -> String { - let mut message_value = String::new(); let mut index = item_index; - let precision_item = 0x12; + const PRECISION_ITEM: u8 = 0x12; // Increment index get the actual firehose item data - if item_type == &precision_item { + if item_type == PRECISION_ITEM { index += 1; if index > message_values.len() { return format!("Index out of bounds for FirehoseItemInfo Vec. Got adjusted index {}, Vec size is {}. This should not have happened", index, message_values.len()); } } - let masked_hash_type = 0xf2; + const MASKED_HASH_TYPE: u8 = 0xf2; // Check if the log value is hashed or marked private - if (format_string.contains("mask.hash") && message_values[index].item_type == masked_hash_type) + if (format_string.contains("mask.hash") && message_values[index].item_type == MASKED_HASH_TYPE) || message_values[index].message_strings == "" { return message_values[index].message_strings.to_owned(); } // Check if log value contains one the supported decoders - if format_string.contains("BOOL") { - message_value = uppercase_bool(&message_values[index].message_strings); + let message_value: Result> = if format_string.contains("BOOL") { + Ok(uppercase_bool(&message_values[index].message_strings)) } else if format_string.contains("bool") { - message_value = lowercase_bool(&message_values[index].message_strings); + Ok(lowercase_bool(&message_values[index].message_strings)) } else if format_string.contains("uuid_t") { - message_value = parse_uuid(&message_values[index].message_strings); + Ok(parse_uuid(&message_values[index].message_strings)) } else if format_string.contains("darwin.errno") { - message_value = errno_codes(&message_values[index].message_strings); + Ok(errno_codes(&message_values[index].message_strings)) } else if format_string.contains("darwin.mode") { - message_value = permission(&message_values[index].message_strings); + Ok(permission(&message_values[index].message_strings)) } else if format_string.contains("odtypes:ODError") { - message_value = errors(&message_values[index].message_strings); + Ok(errors(&message_values[index].message_strings)) } else if format_string.contains("odtypes:mbridtype") { - message_value = member_id_type(&message_values[index].message_strings); + Ok(member_id_type(&message_values[index].message_strings)) } else if format_string.contains("odtypes:mbr_details") { - message_value = member_details(&message_values[index].message_strings); + Ok(member_details(&message_values[index].message_strings)) } else if format_string.contains("odtypes:nt_sid_t") { - message_value = sid_details(&message_values[index].message_strings); + Ok(sid_details(&message_values[index].message_strings)) } else if format_string.contains("location:CLClientAuthorizationStatus") { - message_value = client_authorization_status(&message_values[index].message_strings) + Ok(client_authorization_status( + &message_values[index].message_strings, + )) } else if format_string.contains("location:CLDaemonStatus_Type::Reachability") { - message_value = daemon_status_type(&message_values[index].message_strings); + Ok(daemon_status_type(&message_values[index].message_strings)) } else if format_string.contains("location:CLSubHarvesterIdentifier") { - message_value = subharvester_identifier(&message_values[index].message_strings); + Ok(subharvester_identifier( + &message_values[index].message_strings, + )) } else if format_string.contains("location:SqliteResult") { - message_value = sqlite(&message_values[index].message_strings); + Ok(sqlite(&message_values[index].message_strings)) } else if format_string.contains("location:_CLClientManagerStateTrackerState") { - message_value = client_manager_state_tracker_state(&message_values[index].message_strings); + Ok(client_manager_state_tracker_state( + &message_values[index].message_strings, + )) } else if format_string.contains("location:_CLLocationManagerStateTrackerState") { - message_value = - location_manager_state_tracker_state(&message_values[index].message_strings); + Ok(location_manager_state_tracker_state( + &message_values[index].message_strings, + )) } else if format_string.contains("network:in6_addr") { - message_value = ipv_six(&message_values[index].message_strings); + Ok(ipv_six(&message_values[index].message_strings)) } else if format_string.contains("network:in_addr") { - message_value = ipv_four(&message_values[index].message_strings); + Ok(ipv_four(&message_values[index].message_strings)) } else if format_string.contains("network:sockaddr") { - message_value = sockaddr(&message_values[index].message_strings); + Ok(sockaddr(&message_values[index].message_strings)) } else if format_string.contains("time_t") { - message_value = parse_time(&message_values[index].message_strings); + Ok(parse_time(&message_values[index].message_strings)) } else if format_string.contains("mdns:dnshdr") { - message_value = parse_dns_header(&message_values[index].message_strings); + parse_dns_header(&message_values[index].message_strings) } else if format_string.contains("mdns:rd.svcb") { - message_value = get_service_binding(&message_values[index].message_strings); + get_service_binding(&message_values[index].message_strings) } else if format_string.contains("location:IOMessage") { - message_value = io_message(&message_values[index].message_strings); + Ok(io_message(&message_values[index].message_strings)) } else if format_string.contains("mdnsresponder:domain_name") { - message_value = get_domain_name(&message_values[index].message_strings); + get_domain_name(&message_values[index].message_strings) } else if format_string.contains("mdnsresponder:mac_addr") { - message_value = get_dns_mac_addr(&message_values[index].message_strings); + get_dns_mac_addr(&message_values[index].message_strings) } else if format_string.contains("mdnsresponder:ip_addr") { - message_value = dns_ip_addr(&message_values[index].message_strings); + dns_ip_addr(&message_values[index].message_strings) } else if format_string.contains("mdns:addrmv") { - message_value = dns_addrmv(&message_values[index].message_strings); + Ok(dns_addrmv(&message_values[index].message_strings)) } else if format_string.contains("mdns:rrtype") { - message_value = dns_records(&message_values[index].message_strings); + Ok(dns_records(&message_values[index].message_strings)) } else if format_string.contains("mdns:nreason") { - message_value = dns_reason(&message_values[index].message_strings); + Ok(dns_reason(&message_values[index].message_strings)) } else if format_string.contains("mdns:protocol") { - message_value = dns_protocol(&message_values[index].message_strings); + Ok(dns_protocol(&message_values[index].message_strings)) } else if format_string.contains("mdns:dns.idflags") { - message_value = dns_idflags(&message_values[index].message_strings); + Ok(dns_idflags(&message_values[index].message_strings)) } else if format_string.contains("mdns:dns.counts") { - message_value = dns_counts(&message_values[index].message_strings); + Ok(dns_counts(&message_values[index].message_strings)) } else if format_string.contains("mdns:yesno") { - message_value = dns_yes_no(&message_values[index].message_strings); + Ok(dns_yes_no(&message_values[index].message_strings)) } else if format_string.contains("mdns:acceptable") { - message_value = dns_acceptable(&message_values[index].message_strings); + Ok(dns_acceptable(&message_values[index].message_strings)) } else if format_string.contains("mdns:gaiopts") { - message_value = dns_getaddrinfo_opts(&message_values[index].message_strings); + Ok(dns_getaddrinfo_opts(&message_values[index].message_strings)) + } else { + Ok(String::new()) + }; + + match message_value { + Ok(value) => value, + Err(e) => { + log::error!("[macos-unifiedlogs] Failed to decode log object. Error: {e:?}"); + e.to_string() + } } - message_value } #[cfg(test)] @@ -143,7 +159,7 @@ mod tests { let test_type = 0; let test_index = 0; - let results = check_objects(test_format, &vec![test_item_info], &test_type, test_index); + let results = check_objects(test_format, &[test_item_info], test_type, test_index); assert_eq!(results, "true") } @@ -158,7 +174,7 @@ mod tests { let test_type = 0; let test_index = 0; - let results = check_objects(test_format, &vec![test_item_info], &test_type, test_index); + let results = check_objects(test_format, &[test_item_info], test_type, test_index); assert_eq!(results, "YES") } @@ -173,7 +189,7 @@ mod tests { let test_type = 50; // 0x32 let test_index = 0; - let results = check_objects(test_format, &vec![test_item_info], &test_type, test_index); + let results = check_objects(test_format, &[test_item_info], test_type, test_index); assert_eq!(results, "user: -2@/Local/Default"); } @@ -188,7 +204,7 @@ mod tests { let test_type = 50; // 0x32 let test_index = 0; - let results = check_objects(test_format, &vec![test_item_info], &test_type, test_index); + let results = check_objects(test_format, &[test_item_info], test_type, test_index); assert_eq!(results, "85957E1D36C44ED286A80657BCDDE293") } @@ -203,7 +219,7 @@ mod tests { let test_type = 50; // 0x32 let test_index = 0; - let results = check_objects(test_format, &vec![test_item_info], &test_type, test_index); + let results = check_objects(test_format, &[test_item_info], test_type, test_index); assert_eq!(results, "") } @@ -218,7 +234,7 @@ mod tests { let test_type = 242; // 0x32 let test_index = 0; - let results = check_objects(test_format, &vec![test_item_info], &test_type, test_index); + let results = check_objects(test_format, &[test_item_info], test_type, test_index); assert_eq!(results, "hash") } } diff --git a/src/decoders/dns.rs b/src/decoders/dns.rs index f4ae14e..a6b0334 100644 --- a/src/decoders/dns.rs +++ b/src/decoders/dns.rs @@ -5,113 +5,102 @@ // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and limitations under the License. -use super::network::{get_ip_four, get_ip_six}; +use super::{ + network::{get_ip_four, get_ip_six}, + DecoderError, +}; use crate::util::{decode_standard, extract_string, extract_string_size}; use byteorder::{BigEndian, WriteBytesExt}; use log::{error, warn}; use nom::{ - bits, bytes::complete::take, - combinator::{map, verify}, - multi::many0, + combinator::{fail, map, map_parser, verify}, + multi::{fold_many0, many0}, number::complete::{be_u128, be_u16, be_u32, be_u8, le_u32}, sequence::tuple, + IResult, }; use std::{ + fmt::Write, mem::size_of, net::{Ipv4Addr, Ipv6Addr}, }; /// Parse the DNS header -pub(crate) fn parse_dns_header(data: &str) -> String { - let decoded_data_result = decode_standard(data); - let decoded_data = match decoded_data_result { - Ok(result) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to base64 decode dns header data {}, error: {:?}", - data, err - ); - return String::from("Failed to base64 decode DNS header details"); - } - }; +pub(crate) fn parse_dns_header(data: &str) -> Result> { + let decoded_data = decode_standard(data).map_err(|_| DecoderError::Parse { + input: data.as_bytes(), + parser_name: "dns header", + message: "Failed to base64 decode DNS header details", + })?; + + let (_, message) = get_dns_header(&decoded_data).map_err(|_| DecoderError::Parse { + input: data.as_bytes(), + parser_name: "dns header", + message: "Failed to parse DNS header details", + })?; + + Ok(message) +} - let message_result = get_dns_header(&decoded_data); - match message_result { - Ok((_, result)) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to get dns header structure: {:?}", - err - ); - format!("Failed to get dns header: {}", data) - } +fn remove_err_offset( + error_with_offset: nom::Err>, +) -> nom::Err> { + use nom::Err; + match error_with_offset { + Err::Error(e) => Err::Error(nom::error::Error { + input: e.input.0, + code: e.code, + }), + Err::Failure(e) => Err::Failure(nom::error::Error { + input: e.input.0, + code: e.code, + }), + Err::Incomplete(e) => Err::Incomplete(e), } } /// Get the DNS header data -fn get_dns_header(data: &[u8]) -> nom::IResult<&[u8], String> { - let (dns_data, id_data) = take(size_of::())(data)?; - let (dns_data, flag_data) = take(size_of::())(dns_data)?; - - let (_, id) = be_u16(id_data)?; - - let message_result = get_dns_flags(flag_data); - let message = match message_result { - Ok(result) => result.1, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to parse DNS header flags. Error: {:?}", - err - ); - String::from("Failed to parse DNS header") - } - }; - - let message_result = parse_counts(dns_data); - let count_message = match message_result { - Ok((_, result)) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to parse DNS header counts. Error: {:?}", - err - ); - String::from("Failed to parse DNS header counts") - } - }; +fn get_dns_header(input: &[u8]) -> IResult<&[u8], String> { + let (input, id) = be_u16(input)?; + let (input, flag_data) = take(size_of::())(input)?; + let ((_, _), message) = get_dns_flags(flag_data).map_err(remove_err_offset)?; let (_, flags) = be_u16(flag_data)?; + let (input, count_message) = parse_counts(input)?; + let header_message = format!( "Query ID: {:#X?}, Flags: {:#X?} {}, {}", id, flags, message, count_message ); - Ok((dns_data, header_message)) + Ok((input, header_message)) } /// Parse the DNS bit flags -fn get_dns_flags(data: &[u8]) -> nom::IResult<(&[u8], usize), String> { +fn get_dns_flags(input: &[u8]) -> IResult<(&[u8], usize), String> { + // https://en.wikipedia.org/wiki/Domain_Name_System#DNS_message_format + const QR: usize = 1; + const OPCODE: usize = 4; + const AA: usize = 1; + const TC: usize = 1; + const RD: usize = 1; + const RA: usize = 1; + const Z: usize = 3; + const RCODE: usize = 4; + + type RET<'a> = ((&'a [u8], usize), u8); + use nom::bits::complete::take as bits; // Have to work with bits instead of bytes for the DNS flags - let ((flag_data, offset), query_flag): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((data, 0))?; - let ((flag_data, offset), opcode): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((flag_data, offset))?; - let ((flag_data, offset), authoritative_flag): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((flag_data, offset))?; - let ((flag_data, offset), truncation_flag): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((flag_data, offset))?; - - let ((flag_data, offset), recursion_desired): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((flag_data, offset))?; - let ((flag_data, offset), recursion_available): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((flag_data, offset))?; - - let reserved_size: usize = 3; - let ((flag_data, offset), _reserved): ((&[u8], usize), u8) = - bits::complete::take(reserved_size)((flag_data, offset))?; - let ((flag_data, _), response_code): ((&[u8], usize), u8) = - bits::complete::take(size_of::())((flag_data, offset))?; + let ((input, offset), query): RET<'_> = bits(QR)((input, 0))?; + let ((input, offset), opcode): RET<'_> = bits(OPCODE)((input, offset))?; + let ((input, offset), authoritative_flag): RET<'_> = bits(AA)((input, offset))?; + let ((input, offset), truncation_flag): RET<'_> = bits(TC)((input, offset))?; + let ((input, offset), recursion_desired): RET<'_> = bits(RD)((input, offset))?; + let ((input, offset), recursion_available): RET<'_> = bits(RA)((input, offset))?; + let ((input, offset), _reserved): RET<'_> = bits(Z)((input, offset))?; + let ((input, _), response_code): RET<'_> = bits(RCODE)((input, offset))?; let opcode_message = match opcode { 0 => "QUERY", @@ -147,7 +136,7 @@ fn get_dns_flags(data: &[u8]) -> nom::IResult<(&[u8], usize), String> { Recursion Available: {}, Response Code: {}", opcode_message, - query_flag, + query, authoritative_flag, truncation_flag, recursion_desired, @@ -155,118 +144,86 @@ fn get_dns_flags(data: &[u8]) -> nom::IResult<(&[u8], usize), String> { response_message ); - Ok(((flag_data, 0), message)) + Ok(((input, 0), message)) } /// Base64 decode the domain name. This is normally masked, but may be shown if private data is enabled -pub(crate) fn get_domain_name(data: &str) -> String { - let decoded_data_result = decode_standard(data); - let decoded_data = match decoded_data_result { - Ok(result) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to base64 decode dns name data {}, error: {:?}", - data, err - ); - return String::from("Failed to base64 decode DNS name details"); - } - }; - - let domain_results = extract_string(&decoded_data); - match domain_results { - Ok((_, results)) => { - let mut clean_domain = String::new(); - let non_domain_chars: Vec = vec!['\n', '\t', '\r']; - for unicode in results.chars() { - // skip non-domain characters and replace with '.' - if non_domain_chars.contains(&unicode) || format!("{:?}", unicode).contains("\\u{") - { - clean_domain.push('.'); - continue; - } - clean_domain.push_str(&String::from(unicode)); - } - clean_domain - } - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to extract domain name from logs: {:?}", - err - ); - String::from("Failed to extract domain name from logs") +pub(crate) fn get_domain_name(input: &str) -> Result> { + let decoded_data = decode_standard(input).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns domain name", + message: "Failed to base64 decode DNS name details", + })?; + + let (_, results) = extract_string(&decoded_data).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns domain name", + message: "Failed to extract domain name from logs", + })?; + + let mut clean_domain = String::new(); + let non_domain_chars = ['\n', '\t', '\r']; + for unicode in results.chars() { + // skip non-domain characters and replace with '.' + if non_domain_chars.contains(&unicode) || format!("{:?}", unicode).contains("\\u{") { + clean_domain.push('.'); + continue; } + clean_domain.push_str(&String::from(unicode)); } + Ok(clean_domain) } /// Parse DNS Service Binding record type -pub(crate) fn get_service_binding(data: &str) -> String { - let decoded_data_result = decode_standard(data); - let decoded_data = match decoded_data_result { - Ok(result) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to base64 decode dns svcb data {}, error: {:?}", - data, err - ); - return String::from("Failed to base64 decode DNS svcb details"); - } - }; - - let message_results = parse_svcb(&decoded_data); - match message_results { - Ok((_, results)) => results, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to parse DNS Service Binding data: {:?}", - err - ); - String::from("Failed to parse DNS Service Binding data") - } - } +pub(crate) fn get_service_binding(input: &str) -> Result> { + let decoded_data = decode_standard(input).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns service binding", + message: "Failed to base64 decode DNS svcb details", + })?; + + let (_, result) = parse_svcb(&decoded_data).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns service binding", + message: "Failed to parse DNS Service Binding data", + })?; + + Ok(result) } /// Parse DNS SVC Binding record -fn parse_svcb(data: &[u8]) -> nom::IResult<&[u8], String> { +fn parse_svcb(input: &[u8]) -> nom::IResult<&[u8], String> { // Format/documentation found at https://datatracker.ietf.org/doc/draft-ietf-dnsop-svcb-https/00/?include_text=1 - let (dns_data, id_data) = take(size_of::())(data)?; - let (dns_data, unknown_type_data) = take(size_of::())(dns_data)?; + let (input, id) = be_u16(input)?; + let (input, unknown_type) = be_u32(input)?; - let (_, unknown_type) = be_u32(unknown_type_data)?; - let dns_over_https = 8388608; // 0x800000 - if unknown_type == dns_over_https { - let (dns_data, url_entry_size) = take(size_of::())(dns_data)?; - let (_, url_size) = be_u8(url_entry_size)?; - return extract_string_size(dns_data, url_size.into()); + const DNS_OVER_HTTPS: u32 = 0x800000; + if unknown_type == DNS_OVER_HTTPS { + let (input, url_size) = be_u8(input)?; + return extract_string_size(input, url_size.into()); } // ALPN = Application Layer Protocol Negotation - let (dns_data, alpn_total_size) = take(size_of::())(dns_data)?; - - let (_, id) = be_u16(id_data)?; - let (_, alpn_size) = be_u8(alpn_total_size)?; - - let (dns_data, alpn_data) = take(alpn_size)(dns_data)?; - let (_, alpn_message) = parse_svcb_alpn(alpn_data)?; - - let (dns_data, ip_message) = parse_svcb_ip(dns_data)?; + let (input, alpn_size) = be_u8(input)?; + let (input, alpn_message) = map_parser(take(alpn_size), parse_svcb_alpn)(input)?; + let (input, ip_message) = parse_svcb_ip(input)?; let message = format!("rdata: {} . {} {}", id, alpn_message, ip_message); - Ok((dns_data, message)) + Ok((input, message)) } /// Parse the Application Layer Protocol Negotation -fn parse_svcb_alpn(dns_data: &[u8]) -> nom::IResult<&[u8], String> { - let mut data = dns_data; +fn parse_svcb_alpn(mut input: &[u8]) -> nom::IResult<&[u8], String> { let mut message = String::from("alpn="); - while !data.is_empty() { - let (alpn_data, entry_size) = take(size_of::())(data)?; - let (_, entry) = be_u8(entry_size)?; - let (alpn_data, alpn_entry) = take(entry)(alpn_data)?; - data = alpn_data; + while !input.is_empty() { + let (i, alpn_entry_size) = be_u8(input)?; + let (i, alpn_entry) = take(alpn_entry_size)(i)?; let (_, alpn_name) = extract_string(alpn_entry)?; - message = format!("{}{},", message, alpn_name); + input = i; + message.push_str(&alpn_name); + message.push(',') } - Ok((data, message)) + Ok((input, message)) } /// Parse the IPs @@ -313,79 +270,66 @@ fn parse_svcb_ip(data: &[u8]) -> nom::IResult<&[u8], String> { } /// Get the MAC Address from the log data -pub(crate) fn get_dns_mac_addr(data: &str) -> String { - let decoded_data_result = decode_standard(data); - let decoded_data = match decoded_data_result { - Ok(result) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to base64 decode dns mac address data {}, error: {:?}", - data, err - ); - return String::from("Failed to base64 decode DNS mac address details"); - } - }; - - let message_results = parse_mac_addr(&decoded_data); - match message_results { - Ok((_, results)) => results, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to parse DNS mac address data: {:?}", - err - ); - String::from("Failed to parse DNS mac address data") - } - } +pub(crate) fn get_dns_mac_addr(input: &str) -> Result> { + let decoded_data = decode_standard(input).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns mac address", + message: "Failed to base64 decode DNS mac address details", + })?; + + let (_, message_results) = parse_mac_addr(&decoded_data).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns mac address", + message: "Failed to parse DNS mac address data", + })?; + + Ok(message_results) } /// Parse the MAC Address -fn parse_mac_addr(dns_data: &[u8]) -> nom::IResult<&[u8], String> { - Ok(map( - many0(map(be_u8, |val| format!("{:02X?}", val))), - |vals| vals.join(":"), - )(dns_data)?) +fn parse_mac_addr(input: &[u8]) -> nom::IResult<&[u8], String> { + fold_many0( + be_u8, + || String::with_capacity(input.len() * 3), // This buffer will not have to reallocate/grow + |mut acc, item| { + if !acc.is_empty() { + acc.push(':'); + } + write!(&mut acc, "{:02X?}", item).ok(); // ignore errors on write in String + acc + }, + )(input) } /// Get IP Address info from log data -pub(crate) fn dns_ip_addr(data: &str) -> String { - let decoded_data_result = decode_standard(data); - let decoded_data = match decoded_data_result { - Ok(result) => result, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to base64 decode dns ip address data {}, error: {:?}", - data, err - ); - return String::from("Failed to base64 decode DNS ip address details"); - } - }; - let message_results = parse_dns_ip_addr(&decoded_data); - match message_results { - Ok((_, results)) => results, - Err(err) => { - error!( - "[macos-unifiedlogs] Failed to parse DNS ip address data: {:?}", - err - ); - String::from("Failed to parse DNS mac address data") - } - } +pub(crate) fn dns_ip_addr(input: &str) -> Result> { + let decoded_data = decode_standard(input).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns ip address", + message: "Failed to base64 decode DNS ip address details", + })?; + + let (_, results) = parse_dns_ip_addr(&decoded_data).map_err(|_| DecoderError::Parse { + input: input.as_bytes(), + parser_name: "dns ip address", + message: "Failed to parse DNS ip address data", + })?; + + Ok(results) } /// Parse IP Address data fn parse_dns_ip_addr(data: &[u8]) -> nom::IResult<&[u8], String> { - let (data, ip_type) = take(size_of::())(data)?; - let (_, ip) = le_u32(ip_type)?; - let ipv4 = 4; - let ipv6 = 6; - if ip == ipv4 { + let (data, ip_version) = le_u32(data)?; + const IPV4: u32 = 4; + const IPV6: u32 = 6; + if ip_version == IPV4 { return get_ip_four(data); - } else if ip == ipv6 { + } else if ip_version == IPV6 { return get_ip_six(data); + } else { + fail(data) } - warn!("[macos-unifiedlogs] Unknown DNS IP Addr type: {}", ip); - Ok((data, format!("Unknown DNS IP Addr type: {}", ip))) } /// Translate DNS add/rmv log values @@ -529,11 +473,9 @@ pub(crate) fn dns_idflags(data: &str) -> String { /// Parse just the DNS flags associated with the DNS header fn parse_idflags(data: &[u8]) -> nom::IResult<&[u8], String> { - let (dns_data, id_data) = take(size_of::())(data)?; + let (dns_data, id) = be_u16(data)?; let flag_results = get_dns_flags(dns_data); - let (_, id) = be_u16(id_data)?; - let message = match flag_results { Ok((_, result)) => result, Err(err) => { @@ -542,6 +484,8 @@ fn parse_idflags(data: &[u8]) -> nom::IResult<&[u8], String> { } }; + // todo: should be the `get_dns_flags` parser that output what can be used as `flags` + // the responsibility for the `dns_data` format knowledge should not be shared into multiple functions let (_, flags) = be_u16(dns_data)?; Ok(( dns_data, @@ -551,6 +495,9 @@ fn parse_idflags(data: &[u8]) -> nom::IResult<&[u8], String> { /// Get just the DNS count data associated with the DNS header pub(crate) fn dns_counts(data: &str) -> String { + // todo: not sure error handling should be handled as strings + // todo: this function + let flags_results = data.parse::(); let flags: u64 = match flags_results { Ok(results) => results, @@ -578,7 +525,7 @@ pub(crate) fn dns_counts(data: &str) -> String { let message_result = parse_counts(&bytes); match message_result { - Ok((_, result)) => result, + Ok((_, result)) => result.to_string(), Err(err) => { error!("[macos-unifiedlogs] Failed to get counts: {:?}", err); data.to_string() @@ -586,26 +533,38 @@ pub(crate) fn dns_counts(data: &str) -> String { } } -/// parse just the DNS count data associated with the DNS header -fn parse_counts(data: &[u8]) -> nom::IResult<&[u8], String> { - let (dns_data, question_data) = take(size_of::())(data)?; - let (dns_data, answer_data) = take(size_of::())(dns_data)?; - let (dns_data, authority_data) = take(size_of::())(dns_data)?; - let (dns_data, additional_data) = take(size_of::())(dns_data)?; +#[derive(Debug, PartialEq)] +struct DnsCounts { + question: u16, + answer: u16, + authority: u16, + additional: u16, +} - let (_, question) = be_u16(question_data)?; - let (_, answer) = be_u16(answer_data)?; - let (_, authority) = be_u16(authority_data)?; - let (_, additional) = be_u16(additional_data)?; +impl std::fmt::Display for DnsCounts { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Question Count: {}, Answer Record Count: {}, Authority Record Count: {}, Additional Record Count: {}", + self.question, self.answer, self.authority, self.additional + ) + } +} - let header_message = format!( - "Question Count: {}, Answer Record Count: {}, Authority Record Count: {}, Additional Record Count: {}", - question, - answer, - authority, - additional); +/// parse just the DNS count data associated with the DNS header +fn parse_counts(data: &[u8]) -> nom::IResult<&[u8], DnsCounts> { + let (input, (question, answer, authority, additional)) = + tuple((be_u16, be_u16, be_u16, be_u16))(data)?; - Ok((dns_data, header_message)) + Ok(( + input, + DnsCounts { + question, + answer, + authority, + additional, + }, + )) } /// Translate DNS yes/no log values @@ -641,21 +600,13 @@ pub(crate) fn dns_getaddrinfo_opts(data: &str) -> String { #[cfg(test)] mod tests { - use crate::{ - decoders::dns::{ - dns_acceptable, dns_addrmv, dns_counts, dns_getaddrinfo_opts, dns_idflags, dns_ip_addr, - dns_protocol, dns_reason, dns_records, dns_yes_no, get_dns_flags, get_dns_header, - get_dns_mac_addr, get_domain_name, get_service_binding, parse_counts, parse_dns_header, - parse_dns_ip_addr, parse_idflags, parse_mac_addr, parse_svcb, parse_svcb_alpn, - parse_svcb_ip, - }, - util::decode_standard, - }; + use super::*; + use crate::util::decode_standard; #[test] fn test_parse_dns_header() { let test_data = "uXMBAAABAAAAAAAA"; - let result = parse_dns_header(test_data); + let result = parse_dns_header(test_data).unwrap(); assert_eq!(result, "Query ID: 0xB973, Flags: 0x100 Opcode: QUERY, \n Query Type: 0,\n Authoritative Answer Flag: 0, \n Truncation Flag: 0, \n Recursion Desired: 1, \n Recursion Available: 0, \n Response Code: No Error, Question Count: 1, Answer Record Count: 0, Authority Record Count: 0, Additional Record Count: 0"); } @@ -676,7 +627,7 @@ mod tests { #[test] fn test_get_domain_name() { let test_data = "AzE0NAMxMDEDMTY4AzE5Mgdpbi1hZGRyBGFycGEA"; - let result = get_domain_name(&test_data); + let result = get_domain_name(test_data).unwrap(); assert_eq!(result, ".144.101.168.192.in-addr.arpa"); } @@ -684,7 +635,7 @@ mod tests { fn test_get_service_binding() { let test_data = "AAEAAAEAAwJoMgAEAAhoEJRAaBCVQAAGACAmBkcAAAAAAAAAAABoEJRAJgZHAAAAAAAAAAAAaBCVQA=="; - let result = get_service_binding(&test_data); + let result = get_service_binding(&test_data).unwrap(); assert_eq!(result, "rdata: 1 . alpn=h2, ipv4 hint:104.16.148.64,104.16.149.64, ipv6 hint:2606:4700::6810:9440,2606:4700::6810:9540"); } @@ -717,11 +668,31 @@ mod tests { assert_eq!(result, "ipv4 hint:104.16.148.64,104.16.149.64, ipv6 hint:2606:4700::6810:9440,2606:4700::6810:9540"); } + #[test] + fn test_parse_svcb_ip_should_not_infine_loop() { + let test_data = [ + // 104.16.148.64 + 0, 4, 0, 4, 104, 16, 148, 64, // + // 2606:4700::6810:9440 + 0, 6, 0, 16, 38, 6, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 16, 148, 64, + // [invalid data] infinite loop (consuming too much) in the previous version + 0, 42, 0, 0, + ]; + + let result = parse_svcb_ip(&test_data); + assert!(result.is_err()); + + // // Previous version would have this behavior : + // let (rest, result) = parse_svcb_ip(&test_data).unwrap(); + // assert_eq!(rest, &[] as &[u8]); + // assert_eq!(result, "ipv4 hint:104.16.148.64, ipv6 hint:2606:4700::6810:9440,"); + } + #[test] fn test_get_dns_mac_addr() { let test_data = "AAAAAAAA"; - let result = get_dns_mac_addr(&test_data); + let result = get_dns_mac_addr(test_data).unwrap(); assert_eq!(result, "00:00:00:00:00:00"); } @@ -737,7 +708,7 @@ mod tests { fn test_dns_ip_addr() { let test_data = "BAAAAMCoZZAAAAAAAAAAAAAAAAA="; - let result = dns_ip_addr(&test_data); + let result = dns_ip_addr(test_data).unwrap(); assert_eq!(result, "192.168.101.144"); } @@ -755,7 +726,7 @@ mod tests { fn test_dns_addrmv() { let test_data = "1"; - let result = dns_addrmv(&test_data); + let result = dns_addrmv(test_data); assert_eq!(result, "add"); } @@ -763,7 +734,7 @@ mod tests { fn test_dns_records() { let test_data = "65"; - let result = dns_records(&test_data); + let result = dns_records(test_data); assert_eq!(result, "HTTPS"); } @@ -771,7 +742,7 @@ mod tests { fn test_dns_reason() { let test_data = "1"; - let result = dns_reason(&test_data); + let result = dns_reason(test_data); assert_eq!(result, "no-data"); } @@ -779,7 +750,7 @@ mod tests { fn test_dns_protocol() { let test_data = "1"; - let result = dns_protocol(&test_data); + let result = dns_protocol(test_data); assert_eq!(result, "UDP"); } @@ -787,7 +758,7 @@ mod tests { fn test_dns_idflags() { let test_data = "2126119168"; - let result = dns_idflags(&test_data); + let result = dns_idflags(test_data); assert_eq!(result, "id: 0x7EBA, flags: 0x100 Opcode: QUERY, \n Query Type: 0,\n Authoritative Answer Flag: 0, \n Truncation Flag: 0, \n Recursion Desired: 1, \n Recursion Available: 0, \n Response Code: No Error"); } @@ -803,7 +774,7 @@ mod tests { fn test_dns_counts() { let test_data = "281474976710656"; - let result = dns_counts(&test_data); + let result = dns_counts(test_data); assert_eq!(result, "Question Count: 1, Answer Record Count: 0, Authority Record Count: 0, Additional Record Count: 0"); } @@ -812,14 +783,22 @@ mod tests { let test_data = vec![0, 1, 0, 0, 0, 0, 0, 0]; let (_, result) = parse_counts(&test_data).unwrap(); - assert_eq!(result, "Question Count: 1, Answer Record Count: 0, Authority Record Count: 0, Additional Record Count: 0"); + assert_eq!( + result, + DnsCounts { + question: 1, + answer: 0, + authority: 0, + additional: 0 + } + ); } #[test] fn test_dns_yes_no() { let test_data = "0"; - let result = dns_yes_no(&test_data); + let result = dns_yes_no(test_data); assert_eq!(result, "no"); } @@ -827,7 +806,7 @@ mod tests { fn test_dns_acceptable() { let test_data = "0"; - let result = dns_acceptable(&test_data); + let result = dns_acceptable(test_data); assert_eq!(result, "unacceptable"); } @@ -835,7 +814,7 @@ mod tests { fn test_dns_getaddrinfo_opts() { let test_data = "8"; - let result = dns_getaddrinfo_opts(&test_data); + let result = dns_getaddrinfo_opts(test_data); assert_eq!(result, "0x8 {use-failover}"); } } diff --git a/src/decoders/location.rs b/src/decoders/location.rs index c7b5b0e..96084d7 100644 --- a/src/decoders/location.rs +++ b/src/decoders/location.rs @@ -423,32 +423,32 @@ fn location_tracker_object(tracker: &LocationTrackerState) -> String { }}", tracker.distance_filter, tracker.desired_accuracy, - lowercase_int_bool(&tracker.updating_location), - lowercase_int_bool(&tracker.requesting_location), - lowercase_int_bool(&tracker.requesting_ranging), - lowercase_int_bool(&tracker.updating_ranging), - lowercase_int_bool(&tracker.updating_heading), + lowercase_int_bool(tracker.updating_location), + lowercase_int_bool(tracker.requesting_location), + lowercase_int_bool(tracker.requesting_ranging), + lowercase_int_bool(tracker.updating_ranging), + lowercase_int_bool(tracker.updating_heading), tracker.heading_filter, - lowercase_int_bool(&tracker.allows_location_prompts), - lowercase_int_bool(&tracker.allows_altered_locations), - lowercase_int_bool(&tracker.dynamic_accuracy), - lowercase_int_bool(&tracker.previous_authorization_status_valid), + lowercase_int_bool(tracker.allows_location_prompts), + lowercase_int_bool(tracker.allows_altered_locations), + lowercase_int_bool(tracker.dynamic_accuracy), + lowercase_int_bool(tracker.previous_authorization_status_valid), tracker.previous_authorization_status, - lowercase_int_bool(&tracker.limits_precision), + lowercase_int_bool(tracker.limits_precision), tracker.activity_type, tracker.pauses_location_updates, - lowercase_int_bool(&tracker.paused), - lowercase_int_bool(&tracker.allows_background_updates), - lowercase_int_bool(&tracker.shows_background_location), - lowercase_int_bool(&tracker.allows_map_correction), - lowercase_int_bool(&tracker.batching_location), - lowercase_int_bool(&tracker.updating_vehicle_speed), - lowercase_int_bool(&tracker.updating_vehicle_heading), - lowercase_int_bool(&tracker.match_info), - lowercase_int_bool(&tracker.ground_altitude), - lowercase_int_bool(&tracker.fusion_info), - lowercase_int_bool(&tracker.courtesy_prompt), - lowercase_int_bool(&tracker.is_authorized_for_widgets), + lowercase_int_bool(tracker.paused), + lowercase_int_bool(tracker.allows_background_updates), + lowercase_int_bool(tracker.shows_background_location), + lowercase_int_bool(tracker.allows_map_correction), + lowercase_int_bool(tracker.batching_location), + lowercase_int_bool(tracker.updating_vehicle_speed), + lowercase_int_bool(tracker.updating_vehicle_heading), + lowercase_int_bool(tracker.match_info), + lowercase_int_bool(tracker.ground_altitude), + lowercase_int_bool(tracker.fusion_info), + lowercase_int_bool(tracker.courtesy_prompt), + lowercase_int_bool(tracker.is_authorized_for_widgets), ) } @@ -546,15 +546,15 @@ pub(crate) fn get_daemon_status_tracker(data: &[u8]) -> nom::IResult<&[u8], Stri r#"{{"thermalLevel": {}, "reachability: "{}", "airplaneMode": {}, "batteryData":{{"wasConnected": {}, "charged": {}, "level": {}, "connected": {}, "chargerType": "{}"}}, "restrictedMode": {}, "batterySaverModeEnabled": {}, "push_service":{}}}"#, thermal_level, reachability_str, - lowercase_int_bool(&airplane), - lowercase_int_bool(&was_connected), - lowercase_int_bool(&charged), + lowercase_int_bool(airplane), + lowercase_int_bool(was_connected), + lowercase_int_bool(charged), level, - lowercase_int_bool(&connected), + lowercase_int_bool(connected), charger_type_str, - lowercase_int_bool(&restricted), - lowercase_int_bool(&battery_saver), - lowercase_int_bool(&push_service) + lowercase_int_bool(restricted), + lowercase_int_bool(battery_saver), + lowercase_int_bool(push_service) ); Ok((location_data, message)) diff --git a/src/decoders/mod.rs b/src/decoders/mod.rs index 32c1a90..e89e9b9 100644 --- a/src/decoders/mod.rs +++ b/src/decoders/mod.rs @@ -14,3 +14,36 @@ mod network; mod opendirectory; mod time; mod uuid; + +pub enum DecoderError<'a> { + Parse { + input: &'a [u8], + parser_name: &'a str, + message: &'a str, + }, +} + +impl<'a> std::error::Error for DecoderError<'a> {} + +impl<'a> std::fmt::Display for DecoderError<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Parse { message, .. } => write!(f, "{message}"), + } + } +} + +impl<'a> std::fmt::Debug for DecoderError<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Parse { + parser_name, + message, + input, + } => write!( + f, + "Failed at {parser_name} parser, data {input:?}: {message}" + ), + } + } +} diff --git a/src/decoders/network.rs b/src/decoders/network.rs index 88b2fbe..c28757a 100644 --- a/src/decoders/network.rs +++ b/src/decoders/network.rs @@ -9,6 +9,7 @@ use crate::util::decode_standard; use log::{error, warn}; use nom::{ bytes::complete::take, + combinator::map, number::complete::{be_u128, be_u16, be_u32, be_u8}, }; use std::net::Ipv6Addr; @@ -150,21 +151,13 @@ fn get_sockaddr_data(data: &[u8]) -> nom::IResult<&[u8], String> { } /// Get the IPv4 data -pub(crate) fn get_ip_four(data: &[u8]) -> nom::IResult<&[u8], String> { - let (ip_data, ip_addr_data) = take(size_of::())(data)?; - let (_, ip) = be_u32(ip_addr_data)?; - let ip = Ipv4Addr::from(ip); - - Ok((ip_data, ip.to_string())) +pub(crate) fn get_ip_four(input: &[u8]) -> nom::IResult<&[u8], String> { + map(be_u32, |val| Ipv4Addr::from(val).to_string())(input) } /// Get the IPv6 data -pub(crate) fn get_ip_six(data: &[u8]) -> nom::IResult<&[u8], String> { - let (ip_data, ip_addr_data) = take(size_of::())(data)?; - let (_, ip) = be_u128(ip_addr_data)?; - let ip = Ipv6Addr::from(ip); - - Ok((ip_data, ip.to_string())) +pub(crate) fn get_ip_six(input: &[u8]) -> nom::IResult<&[u8], String> { + map(be_u128, |val| Ipv6Addr::from(val).to_string())(input) } #[cfg(test)] diff --git a/src/error.rs b/src/error.rs index 587f8be..5b0e8bd 100755 --- a/src/error.rs +++ b/src/error.rs @@ -86,13 +86,13 @@ impl std::error::Error for ParserError {} impl fmt::Display for ParserError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ParserError::Path => write!(f, "Failed to open file path"), - ParserError::Dir => write!(f, "Failed to open directory path"), - ParserError::Tracev3Parse => write!(f, "Failed to parse tracev3 file"), - ParserError::Read => write!(f, "Failed to read file"), - ParserError::Timesync => write!(f, "Failed to parse timesync file"), - ParserError::Dsc => write!(f, "Failed to parse dsc file"), - ParserError::UUIDText => write!(f, "Failedto parse UUIDtext file"), + Self::Path => write!(f, "Failed to open file path"), + Self::Dir => write!(f, "Failed to open directory path"), + Self::Tracev3Parse => write!(f, "Failed to parse tracev3 file"), + Self::Read => write!(f, "Failed to read file"), + Self::Timesync => write!(f, "Failed to parse timesync file"), + Self::Dsc => write!(f, "Failed to parse dsc file"), + Self::UUIDText => write!(f, "Failedto parse UUIDtext file"), } } } diff --git a/src/message.rs b/src/message.rs index 314d485..612f2db 100755 --- a/src/message.rs +++ b/src/message.rs @@ -164,7 +164,7 @@ pub fn format_firehose_log_message( let results = parse_type_formatter( formatter_string, item_message, - &item_message[item_index].item_type, + item_message[item_index].item_type, item_index, ); match results { @@ -204,7 +204,7 @@ pub fn format_firehose_log_message( let results = parse_formatter( formatter_string, item_message, - &item_message[item_index].item_type, + item_message[item_index].item_type, item_index, ); match results { @@ -214,9 +214,9 @@ pub fn format_firehose_log_message( } } - let precision_items = [0x10, 0x12]; // dynamic precision item types? - // If the item message was a precision type increment to actual value - if precision_items.contains(&item_message[item_index].item_type) { + const PRECISION_ITEMS: [u8; 2] = [0x10, 0x12]; // dynamic precision item types? + // If the item message was a precision type increment to actual value + if PRECISION_ITEMS.contains(&item_message[item_index].item_type) { item_index += 1; } @@ -267,14 +267,14 @@ pub fn format_firehose_log_message( fn parse_formatter<'a>( formatter: &'a str, message_value: &'a [FirehoseItemInfo], - item_type: &'a u8, + item_type: u8, item_index: usize, ) -> nom::IResult<&'a str, String> { let mut index = item_index; - let precision_items = [0x10, 0x12]; + const PRECISION_ITEMS: [u8; 2] = [0x10, 0x12]; let mut precision_value = 0; - if precision_items.contains(item_type) { + if PRECISION_ITEMS.contains(&item_type) { precision_value = message_value[index].item_size as usize; index += 1; @@ -342,8 +342,8 @@ fn parse_formatter<'a>( if formatter_message.starts_with('*') { // Also seen number type value 0 used for dynamic width/precision value - let dynamic_precision_value = 0x0; - if item_type == &dynamic_precision_value && message_value[index].item_size == 0 { + const DYNAMIC_PRECISION_VALUE: u8 = 0x0; + if item_type == DYNAMIC_PRECISION_VALUE && message_value[index].item_size == 0 { precision_value = message_value[index].item_size as usize; index += 1; if index >= message_value.len() { @@ -470,7 +470,7 @@ fn parse_formatter<'a>( fn parse_type_formatter<'a>( formatter: &'a str, message_value: &'a [FirehoseItemInfo], - item_type: &'a u8, + item_type: u8, item_index: usize, ) -> nom::IResult<&'a str, String> { let (format, format_type) = take_until("}")(formatter)?; @@ -1103,12 +1103,11 @@ mod tests { #[test] fn test_format_firehose_log_message() { let test_data = String::from("opendirectoryd (build %{public}s) launched..."); - let mut item_message: Vec = Vec::new(); - item_message.push(FirehoseItemInfo { + let item_message = vec![FirehoseItemInfo { message_strings: String::from("796.100"), item_type: 34, item_size: 0, - }); + }]; let message_re = Regex::new(r"(%(?:(?:\{[^}]+}?)(?:[-+0#]{0,5})(?:\d+|\*)?(?:\.(?:\d+|\*))?(?:h|hh|l|ll|w|I|z|t|q|I32|I64)?[cmCdiouxXeEfgGaAnpsSZP@%}]|(?:[-+0 #]{0,5})(?:\d+|\*)?(?:\.(?:\d+|\*))?(?:h|hh|l||q|t|ll|w|I|z|I32|I64)?[cmCdiouxXeEfgGaAnpsSZP@%]))").unwrap(); let log_string = format_firehose_log_message(test_data, &item_message, &message_re); @@ -1131,7 +1130,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1141,7 +1140,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1151,7 +1150,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1162,7 +1161,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1172,7 +1171,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1183,7 +1182,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1194,7 +1193,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_float, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1204,7 +1203,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_float, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1214,7 +1213,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_float, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1225,7 +1224,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_float, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1236,7 +1235,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_int, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1247,7 +1246,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_float, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1258,7 +1257,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_float, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1269,7 +1268,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_string, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1280,7 +1279,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_string, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1298,7 +1297,7 @@ mod tests { let (_, formatted_results) = parse_formatter( test_string, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1321,7 +1320,7 @@ mod tests { let (_, formatted_results) = parse_type_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1341,7 +1340,7 @@ mod tests { let (_, formatted_results) = parse_type_formatter( test_format, &test_message, - &test_message[0].item_type, + test_message[0].item_type, item_index, ) .unwrap(); @@ -1405,7 +1404,7 @@ mod tests { test_format, test_width, test_precision, - &test_type, + test_type, plus_minus, hashtag, ); @@ -1424,7 +1423,7 @@ mod tests { test_format, test_width, test_precision, - &test_type, + test_type, plus_minus, hashtag, ); @@ -1439,7 +1438,7 @@ mod tests { let plus_minus = false; let hashtag = false; let formatted_results = - format_left(test_format, test_precision, &test_type, plus_minus, hashtag); + format_left(test_format, test_precision, test_type, plus_minus, hashtag); assert_eq!(formatted_results, "2"); } @@ -1451,7 +1450,7 @@ mod tests { let plus_minus = false; let hashtag = false; let formatted_results = - format_right(test_format, test_precision, &test_type, plus_minus, hashtag); + format_right(test_format, test_precision, test_type, plus_minus, hashtag); assert_eq!(formatted_results, "2"); } diff --git a/src/util.rs b/src/util.rs index 59fb846..e34747f 100755 --- a/src/util.rs +++ b/src/util.rs @@ -32,8 +32,8 @@ pub(crate) fn padding_size_four(data: u64) -> u64 { /// Extract a size based on provided string size from Firehose string item entries pub(crate) fn extract_string_size(data: &[u8], message_size: u64) -> nom::IResult<&[u8], String> { - let null_string = 0; - if message_size == null_string { + const NULL_STRING: u64 = 0; + if message_size == NULL_STRING { return Ok((data, String::from("(null)"))); } @@ -69,11 +69,11 @@ pub(crate) fn extract_string(data: &[u8]) -> nom::IResult<&[u8], String> { let last_value = data.last(); match last_value { Some(value) => { - let has_end_of_string: u8 = 0; + const NULL_BYTE: u8 = 0; // If message data does not end with end of string character (0) // just grab everything and convert what we have to string - if value != &has_end_of_string { + if value != &NULL_BYTE { let (input, path) = take(data.len())(data)?; let path_string = from_utf8(path); match path_string {