From 2fbf203c538c939fe6265b3eb529de1bc858171f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=84=CA=9C=CA=80=C9=AAs=E1=B4=9B=E1=B4=8F=E1=B4=98?= =?UTF-8?q?=CA=9C=E1=B4=87=CA=80=20=E1=B4=8D=E1=B4=87=C9=B4=C9=A2?= Date: Sun, 23 Aug 2026 23:23:23 -0400 Subject: [PATCH] Decode libpcap's error messages lossily --- CHANGELOG.md | 4 ++++ src/lib.rs | 18 ++++++++++++++---- src/linktype.rs | 7 +++++++ 3 files changed, 25 insertions(+), 4 deletions(-) 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]