diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f5cb309..f4950c46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ `LOOPBACK | UP`. - `errno` updated from 0.2 to 0.3. `Error::ErrnoError` carries an `errno::Errno`, so crates that construct or match on it have to move to 0.3 as well. +- Error messages coming from libpcap are decoded lossily. libpcap truncates them at + `PCAP_ERRBUF_SIZE` without regard for character boundaries, so one quoting a long non-ASCII + path used to arrive as `Error::MalformedError` with the message thrown away. It now arrives as + `Error::PcapError`. Device and link-layer type names are still rejected when malformed. - `windows-sys` updated from 0.36 to 0.61. `HANDLE` is a raw pointer there rather than an `isize`, which changes the signature of `Capture::get_event` on Windows. A raw pointer is not `Send`, so a type of your own that stores the returned `HANDLE` no longer derives `Send` and can no longer diff --git a/src/lib.rs b/src/lib.rs index d99fc976..a29a7b6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,10 +135,19 @@ pub enum Error { impl Error { unsafe fn new(ptr: *const libc::c_char) -> Error { - match unsafe { cstr_to_string(ptr) } { - Err(e) => e as Error, - Ok(string) => PcapError(string.unwrap_or_default()), + if ptr.is_null() { + return PcapError(String::new()); } + + // libpcap truncates its messages at PCAP_ERRBUF_SIZE without regard for character + // boundaries, so one quoting a long path can end in the middle of a UTF-8 sequence. + // Take such a message lossily rather than lose it. Strings that are not error messages + // still go through cstr_to_string, which rejects the malformed ones. + PcapError( + unsafe { CStr::from_ptr(ptr as _) } + .to_string_lossy() + .into_owned(), + ) } fn with_errbuf(func: F) -> Result @@ -252,7 +261,8 @@ mod tests { fn test_error_invalid_utf8() { let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f]; let error = unsafe { Error::new(&bytes as *const _ as _) }; - assert!(matches!(error, Error::MalformedError(_))); + // The message is kept, with the bytes that are not valid UTF-8 replaced. + assert_eq!(error, Error::PcapError("x\u{fffd}\u{fffd}".to_string())); } #[test] diff --git a/src/linktype.rs b/src/linktype.rs index bf200296..70ed7bcc 100644 --- a/src/linktype.rs +++ b/src/linktype.rs @@ -186,6 +186,13 @@ mod tests { let linktype_name = Linktype::ARCNET_LINUX.get_name().unwrap(); assert_eq!(&linktype_name, name); + + let ctx = raw::pcap_datalink_val_to_name_context(); + ctx.checkpoint(); + ctx.expect().return_once(|_| std::ptr::null()); + + let err = Linktype::ARCNET_LINUX.get_name().unwrap_err(); + assert_eq!(err, Error::InvalidLinktype); } #[test]