Skip to content

Commit c347341

Browse files
committed
Change error-chain to thiserror
1 parent 106e112 commit c347341

16 files changed

+211
-108
lines changed

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ travis-ci = { repository = "mullvad/pfctl-rs" }
1616

1717
[dependencies]
1818
errno = "0.2"
19-
error-chain = "0.12"
2019
ioctl-sys = "0.6.0"
2120
libc = "0.2.29"
2221
derive_builder = "0.9"
2322
ipnetwork = "0.16"
23+
thiserror = "1"
2424

2525
[dev-dependencies]
2626
assert_matches = "1.1.0"
27+
error-chain = "0.12"
2728
uuid = { version = "0.8", features = ["v4"] }
2829
scopeguard = "1.0"

examples/enable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn run() -> Result<()> {
2121
// Try to enable the firewall. Equivalent to the CLI command "pfctl -e".
2222
match pf.enable() {
2323
Ok(_) => println!("Enabled PF"),
24-
Err(pfctl::Error(pfctl::ErrorKind::StateAlreadyActive, _)) => (),
24+
Err(pfctl::Error::StateAlreadyActive(_)) => (),
2525
err => err.chain_err(|| "Unable to enable PF")?,
2626
}
2727
Ok(())

src/lib.rs

+122-43
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,14 @@
5959
6060
#![deny(rust_2018_idioms)]
6161

62-
#[macro_use]
63-
pub extern crate error_chain;
64-
6562
use std::{
6663
ffi::CStr,
64+
fmt::{Debug, Display},
6765
fs::File,
6866
mem,
6967
os::unix::io::{AsRawFd, RawFd},
7068
};
69+
use thiserror::Error;
7170

7271
pub use ipnetwork;
7372

@@ -92,40 +91,117 @@ pub use crate::ruleset::*;
9291
mod transaction;
9392
pub use crate::transaction::*;
9493

95-
mod errors {
96-
error_chain! {
97-
errors {
98-
DeviceOpenError(s: &'static str) {
99-
description("Unable to open PF device file")
100-
display("Unable to open PF device file at '{}'", s)
101-
}
102-
InvalidArgument(s: &'static str) {
103-
display("Invalid argument: {}", s)
104-
}
105-
StateAlreadyActive {
106-
description("Target state is already active")
107-
}
108-
InvalidRuleCombination(s: String) {
109-
description("Rule contains incompatible values")
110-
display("Incompatible values in rule: {}", s)
111-
}
112-
AnchorDoesNotExist {
113-
display("Anchor does not exist")
94+
#[derive(Error, Debug)]
95+
#[non_exhaustive]
96+
pub enum Error {
97+
#[error("Unable to open PF device file at {}", _0)]
98+
DeviceOpen(&'static str, #[source] ::std::io::Error),
99+
100+
#[error("Target state is already active")]
101+
StateAlreadyActive(#[source] ::std::io::Error),
102+
103+
#[error("Incompatible values in rule: {}", _0)]
104+
InvalidRuleCombination(String),
105+
106+
#[error("Anchor does not exist")]
107+
AnchorDoesNotExist,
108+
109+
#[error("Cstr not null terminated")]
110+
CstrNotTerminated,
111+
112+
#[error("TryCopyTo conversion error")]
113+
Conversion(#[from] TryCopyToErrorWithKind),
114+
115+
#[error("Ioctl Error")]
116+
Ioctl(#[from] ::std::io::Error),
117+
}
118+
119+
#[derive(Error, Debug)]
120+
#[non_exhaustive]
121+
pub enum TryCopyToError {
122+
#[error("Lower port is greater than upper port.")]
123+
InvalidPortRange,
124+
125+
#[error("Insufficient string buffer length")]
126+
InsufficientStringBufferLength,
127+
128+
#[error("String should not contain null byte")]
129+
StringContainsNullByte,
130+
}
131+
132+
#[derive(Debug)]
133+
#[non_exhaustive]
134+
pub enum TryCopyToErrorKind {
135+
InvalidAnchorName,
136+
IncompatibleInterfaceName,
137+
InvalidRouteTarget,
138+
}
139+
140+
impl Display for TryCopyToErrorKind {
141+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142+
match self {
143+
TryCopyToErrorKind::InvalidAnchorName => write!(f, "Invalid anchor name"),
144+
TryCopyToErrorKind::IncompatibleInterfaceName => {
145+
write!(f, "Incompatible interface name")
114146
}
147+
TryCopyToErrorKind::InvalidRouteTarget => write!(f, "Invalid route target"),
148+
}
149+
}
150+
}
151+
152+
#[derive(Debug)]
153+
pub struct TryCopyToErrorWithKind {
154+
pub kind: Option<TryCopyToErrorKind>,
155+
pub source: TryCopyToError,
156+
}
157+
158+
impl Display for TryCopyToErrorWithKind {
159+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160+
if let Some(kind) = self.kind.as_ref() {
161+
Display::fmt(kind, f)
162+
} else {
163+
Display::fmt(&self.source, f)
115164
}
116-
foreign_links {
117-
IoctlError(::std::io::Error);
165+
}
166+
}
167+
168+
impl std::error::Error for TryCopyToErrorWithKind {
169+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
170+
self.kind.as_ref()?;
171+
Some(&self.source)
172+
}
173+
}
174+
175+
impl TryCopyToErrorWithKind {
176+
fn new(kind: TryCopyToErrorKind, err: TryCopyToError) -> Self {
177+
Self {
178+
kind: Some(kind),
179+
source: err,
118180
}
119181
}
120182
}
121-
pub use crate::errors::*;
122183

123-
/// Returns the given input result, except if it is an `Err` matching the given `ErrorKind`,
184+
impl From<TryCopyToError> for TryCopyToErrorWithKind {
185+
fn from(source: TryCopyToError) -> Self {
186+
Self { kind: None, source }
187+
}
188+
}
189+
190+
impl From<TryCopyToError> for Error {
191+
fn from(source: TryCopyToError) -> Self {
192+
Self::Conversion(TryCopyToErrorWithKind { kind: None, source })
193+
}
194+
}
195+
196+
pub type Result<T> = ::std::result::Result<T, Error>;
197+
pub type TryCopyToResult<T> = ::std::result::Result<T, TryCopyToError>;
198+
199+
/// Returns the given input result, except if it is an `Err` matching the given `Error`,
124200
/// then it returns `Ok(())` instead, so the error is ignored.
125201
macro_rules! ignore_error_kind {
126202
($result:expr, $kind:pat) => {
127203
match $result {
128-
Err($crate::Error($kind, _)) => Ok(()),
204+
Err($kind) => Ok(()),
129205
result => result,
130206
}
131207
};
@@ -141,14 +217,18 @@ mod conversion {
141217

142218
/// Internal trait for all types that can try to write their value into another type.
143219
pub trait TryCopyTo<T: ?Sized> {
144-
fn try_copy_to(&self, dst: &mut T) -> crate::Result<()>;
220+
type Result;
221+
222+
fn try_copy_to(&self, dst: &mut T) -> Self::Result;
145223
}
146224
}
147225
use crate::conversion::*;
148226

149227
/// Internal function to safely compare Rust string with raw C string slice
150228
fn compare_cstr_safe(s: &str, cchars: &[std::os::raw::c_char]) -> Result<bool> {
151-
ensure!(cchars.iter().any(|&c| c == 0), "Not null terminated");
229+
if !(cchars.iter().any(|&c| c == 0)) {
230+
return Err(Error::CstrNotTerminated);
231+
}
152232
let cs = unsafe { CStr::from_ptr(cchars.as_ptr()) };
153233
Ok(s.as_bytes() == cs.to_bytes())
154234
}
@@ -174,7 +254,7 @@ impl PfCtl {
174254
/// Same as `enable`, but `StateAlreadyActive` errors are supressed and exchanged for
175255
/// `Ok(())`.
176256
pub fn try_enable(&mut self) -> Result<()> {
177-
ignore_error_kind!(self.enable(), ErrorKind::StateAlreadyActive)
257+
ignore_error_kind!(self.enable(), Error::StateAlreadyActive(_))
178258
}
179259

180260
/// Tries to disable PF. If the firewall is already disabled it will return an
@@ -186,7 +266,7 @@ impl PfCtl {
186266
/// Same as `disable`, but `StateAlreadyActive` errors are supressed and exchanged for
187267
/// `Ok(())`.
188268
pub fn try_disable(&mut self) -> Result<()> {
189-
ignore_error_kind!(self.disable(), ErrorKind::StateAlreadyActive)
269+
ignore_error_kind!(self.disable(), Error::StateAlreadyActive(_))
190270
}
191271

192272
/// Tries to determine if PF is enabled or not.
@@ -201,7 +281,7 @@ impl PfCtl {
201281

202282
pfioc_rule.rule.action = kind.into();
203283
name.try_copy_to(&mut pfioc_rule.anchor_call[..])
204-
.chain_err(|| ErrorKind::InvalidArgument("Invalid anchor name"))?;
284+
.map_err(|e| TryCopyToErrorWithKind::new(TryCopyToErrorKind::InvalidAnchorName, e))?;
205285

206286
ioctl_guard!(ffi::pf_insert_rule(self.fd(), &mut pfioc_rule))?;
207287
Ok(())
@@ -210,7 +290,7 @@ impl PfCtl {
210290
/// Same as `add_anchor`, but `StateAlreadyActive` errors are supressed and exchanged for
211291
/// `Ok(())`.
212292
pub fn try_add_anchor(&mut self, name: &str, kind: AnchorKind) -> Result<()> {
213-
ignore_error_kind!(self.add_anchor(name, kind), ErrorKind::StateAlreadyActive)
293+
ignore_error_kind!(self.add_anchor(name, kind), Error::StateAlreadyActive(_))
214294
}
215295

216296
pub fn remove_anchor(&mut self, name: &str, kind: AnchorKind) -> Result<()> {
@@ -222,10 +302,7 @@ impl PfCtl {
222302
/// Same as `remove_anchor`, but `AnchorDoesNotExist` errors are supressed and exchanged for
223303
/// `Ok(())`.
224304
pub fn try_remove_anchor(&mut self, name: &str, kind: AnchorKind) -> Result<()> {
225-
ignore_error_kind!(
226-
self.remove_anchor(name, kind),
227-
ErrorKind::AnchorDoesNotExist
228-
)
305+
ignore_error_kind!(self.remove_anchor(name, kind), Error::AnchorDoesNotExist)
229306
}
230307

231308
// TODO(linus): Make more generic. No hardcoded ADD_TAIL etc.
@@ -236,7 +313,7 @@ impl PfCtl {
236313
pfioc_rule.ticket = utils::get_ticket(self.fd(), &anchor, AnchorKind::Filter)?;
237314
anchor
238315
.try_copy_to(&mut pfioc_rule.anchor[..])
239-
.chain_err(|| ErrorKind::InvalidArgument("Invalid anchor name"))?;
316+
.map_err(|e| TryCopyToErrorWithKind::new(TryCopyToErrorKind::InvalidAnchorName, e))?;
240317
rule.try_copy_to(&mut pfioc_rule.rule)?;
241318

242319
pfioc_rule.action = ffi::pfvar::PF_CHANGE_ADD_TAIL as u32;
@@ -287,7 +364,7 @@ impl PfCtl {
287364

288365
/// Clear states created by rules in anchor.
289366
/// Returns total number of removed states upon success, otherwise
290-
/// ErrorKind::AnchorDoesNotExist if anchor does not exist.
367+
/// Error::AnchorDoesNotExist if anchor does not exist.
291368
pub fn clear_states(&mut self, anchor_name: &str, kind: AnchorKind) -> Result<u32> {
292369
let pfsync_states = self.get_states()?;
293370
if !pfsync_states.is_empty() {
@@ -317,7 +394,9 @@ impl PfCtl {
317394
let mut pfioc_state_kill = unsafe { mem::zeroed::<ffi::pfvar::pfioc_state_kill>() };
318395
interface
319396
.try_copy_to(&mut pfioc_state_kill.psk_ifname)
320-
.chain_err(|| ErrorKind::InvalidArgument("Incompatible interface name"))?;
397+
.map_err(|e| {
398+
TryCopyToErrorWithKind::new(TryCopyToErrorKind::IncompatibleInterfaceName, e)
399+
})?;
321400
ioctl_guard!(ffi::pf_clear_states(self.fd(), &mut pfioc_state_kill))?;
322401
// psk_af holds the number of killed states
323402
Ok(pfioc_state_kill.psk_af as u32)
@@ -342,7 +421,7 @@ impl PfCtl {
342421
/// The return value from closure is transparently passed to the caller.
343422
///
344423
/// - Returns Result<R> from call to closure on match.
345-
/// - Returns `ErrorKind::AnchorDoesNotExist` on mismatch, the closure is not called in that
424+
/// - Returns `Error::AnchorDoesNotExist` on mismatch, the closure is not called in that
346425
/// case.
347426
fn with_anchor_rule<F, R>(&self, name: &str, kind: AnchorKind, f: F) -> Result<R>
348427
where
@@ -359,7 +438,7 @@ impl PfCtl {
359438
return f(pfioc_rule);
360439
}
361440
}
362-
bail!(ErrorKind::AnchorDoesNotExist);
441+
Err(Error::AnchorDoesNotExist)
363442
}
364443

365444
/// Returns global number of states created by all stateful rules (see keep_state)
@@ -419,7 +498,7 @@ mod tests {
419498
let cchars: &[i8] = unsafe { mem::transmute(cstr.as_bytes()) };
420499
assert_matches!(
421500
compare_cstr_safe("Hello", cchars),
422-
Err(ref e) if e.description() == "Not null terminated"
501+
Err(Error::CstrNotTerminated)
423502
);
424503
}
425504

src/macros.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ macro_rules! ioctl_guard {
1616
($func:expr, $already_active:expr) => {
1717
if unsafe { $func } == $crate::macros::IOCTL_ERROR {
1818
let ::errno::Errno(error_code) = ::errno::errno();
19-
let io_error = ::std::io::Error::from_raw_os_error(error_code);
20-
let mut err = Err($crate::ErrorKind::IoctlError(io_error).into());
19+
let err = ::std::io::Error::from_raw_os_error(error_code);
2120
if error_code == $already_active {
22-
err = err.chain_err(|| $crate::ErrorKind::StateAlreadyActive);
21+
Err($crate::Error::StateAlreadyActive(err))
22+
} else {
23+
Err($crate::Error::from(err))
2324
}
24-
err
2525
} else {
2626
Ok(()) as $crate::Result<()>
2727
}

src/pooladdr.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
use crate::{
1010
conversion::{CopyTo, TryCopyTo},
11-
ffi, Interface, Ip, Result,
11+
ffi, Interface, Ip, TryCopyToResult,
1212
};
13-
use std::{mem, ptr, vec::Vec};
13+
use std::{mem, ptr};
1414

1515
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1616
pub struct PoolAddr {
@@ -46,7 +46,9 @@ impl From<Ip> for PoolAddr {
4646
}
4747

4848
impl TryCopyTo<ffi::pfvar::pf_pooladdr> for PoolAddr {
49-
fn try_copy_to(&self, pf_pooladdr: &mut ffi::pfvar::pf_pooladdr) -> Result<()> {
49+
type Result = TryCopyToResult<()>;
50+
51+
fn try_copy_to(&self, pf_pooladdr: &mut ffi::pfvar::pf_pooladdr) -> Self::Result {
5052
self.interface.try_copy_to(&mut pf_pooladdr.ifname)?;
5153
self.ip.copy_to(&mut pf_pooladdr.addr);
5254
Ok(())
@@ -67,7 +69,7 @@ pub struct PoolAddrList {
6769
}
6870

6971
impl PoolAddrList {
70-
pub fn new(pool_addrs: &[PoolAddr]) -> Result<Self> {
72+
pub fn new(pool_addrs: &[PoolAddr]) -> TryCopyToResult<Self> {
7173
let mut pool = Self::init_pool(pool_addrs)?;
7274
Self::link_elements(&mut pool);
7375
let list = Self::create_palist(&mut pool);
@@ -84,7 +86,7 @@ impl PoolAddrList {
8486
self.list
8587
}
8688

87-
fn init_pool(pool_addrs: &[PoolAddr]) -> Result<Vec<ffi::pfvar::pf_pooladdr>> {
89+
fn init_pool(pool_addrs: &[PoolAddr]) -> TryCopyToResult<Vec<ffi::pfvar::pf_pooladdr>> {
8890
let mut pool = Vec::with_capacity(pool_addrs.len());
8991
for pool_addr in pool_addrs {
9092
let mut pf_pooladdr = unsafe { mem::zeroed::<ffi::pfvar::pf_pooladdr>() };

src/rule/endpoint.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ impl From<SocketAddr> for Endpoint {
9292
}
9393

9494
impl TryCopyTo<ffi::pfvar::pf_rule_addr> for Endpoint {
95-
fn try_copy_to(&self, pf_rule_addr: &mut ffi::pfvar::pf_rule_addr) -> Result<()> {
95+
type Result = Result<()>;
96+
97+
fn try_copy_to(&self, pf_rule_addr: &mut ffi::pfvar::pf_rule_addr) -> Self::Result {
9698
self.ip.copy_to(&mut pf_rule_addr.addr);
9799
self.port
98100
.try_copy_to(unsafe { &mut pf_rule_addr.xport.range })?;

src/rule/gid.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
pub use super::uid::{Id, IdUnaryModifier};
9+
pub use super::uid::Id;
1010
use crate::{
1111
conversion::TryCopyTo,
1212
ffi::pfvar::{pf_rule_gid, PF_OP_NONE},
@@ -29,7 +29,9 @@ impl<T: Into<Id>> From<T> for Gid {
2929
}
3030

3131
impl TryCopyTo<pf_rule_gid> for Gid {
32-
fn try_copy_to(&self, pf_rule_gid: &mut pf_rule_gid) -> Result<()> {
32+
type Result = Result<()>;
33+
34+
fn try_copy_to(&self, pf_rule_gid: &mut pf_rule_gid) -> Self::Result {
3335
match self.0 {
3436
Id::Any => {
3537
pf_rule_gid.gid[0] = 0;

0 commit comments

Comments
 (0)