-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add and return PermissionDenied error when not root #5
Open
jpittis
wants to merge
5
commits into
yaa110:master
Choose a base branch
from
jpittis:jpittis/permission-denied
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8c35d38
Reformat all code with cargo fmt
jpittis bfc634d
Create BadExitStatus error so callers can switch on the exit status
jpittis c5d97c0
Use helpers rather than match expression
jpittis d667039
Error message wasnt a valid sentence so should have a period
jpittis 63c81d8
Consistently use and_then pattern accross all code
jpittis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,19 +20,19 @@ | |
|
||
#[macro_use] | ||
extern crate lazy_static; | ||
extern crate regex; | ||
extern crate nix; | ||
extern crate regex; | ||
|
||
pub mod error; | ||
|
||
use std::process::{Command, Output}; | ||
use error::{IPTError, IPTResult}; | ||
use nix::fcntl::{flock, FlockArg}; | ||
use regex::{Match, Regex}; | ||
use error::{IPTResult, IPTError}; | ||
use std::ffi::OsStr; | ||
use std::fs::File; | ||
use std::os::unix::io::AsRawFd; | ||
use nix::fcntl::{flock, FlockArg}; | ||
use std::process::{Command, Output}; | ||
use std::vec::Vec; | ||
use std::ffi::OsStr; | ||
|
||
// List of built-in chains taken from: man 8 iptables | ||
const BUILTIN_CHAINS_FILTER: &'static [&'static str] = &["INPUT", "FORWARD", "OUTPUT"]; | ||
|
@@ -97,24 +97,38 @@ pub fn new(is_ipv6: bool) -> IPTResult<IPTables> { | |
/// Creates a new `IPTables` Result with the command of 'iptables' if `is_ipv6` is `false`, otherwise the command is 'ip6tables'. | ||
#[cfg(target_os = "linux")] | ||
pub fn new(is_ipv6: bool) -> IPTResult<IPTables> { | ||
let cmd = if is_ipv6 { | ||
"ip6tables" | ||
} else { | ||
"iptables" | ||
}; | ||
let cmd = if is_ipv6 { "ip6tables" } else { "iptables" }; | ||
|
||
let version_output = Command::new(cmd).arg("--version").output()?; | ||
let re = Regex::new(r"v(\d+)\.(\d+)\.(\d+)")?; | ||
let version_string = String::from_utf8_lossy(&version_output.stdout).into_owned(); | ||
let versions = re.captures(&version_string).ok_or("invalid version number")?; | ||
let v_major = versions.get(1).ok_or("unable to get major version number")?.as_str().parse::<i32>()?; | ||
let v_minor = versions.get(2).ok_or("unable to get minor version number")?.as_str().parse::<i32>()?; | ||
let v_patch = versions.get(3).ok_or("unable to get patch version number")?.as_str().parse::<i32>()?; | ||
let versions = re | ||
.captures(&version_string) | ||
.ok_or("invalid version number")?; | ||
let v_major = versions | ||
.get(1) | ||
.ok_or("unable to get major version number")? | ||
.as_str() | ||
.parse::<i32>()?; | ||
let v_minor = versions | ||
.get(2) | ||
.ok_or("unable to get minor version number")? | ||
.as_str() | ||
.parse::<i32>()?; | ||
let v_patch = versions | ||
.get(3) | ||
.ok_or("unable to get patch version number")? | ||
.as_str() | ||
.parse::<i32>()?; | ||
|
||
Ok(IPTables { | ||
cmd: cmd, | ||
has_check: (v_major > 1) || (v_major == 1 && v_minor > 4) || (v_major == 1 && v_minor == 4 && v_patch > 10), | ||
has_wait: (v_major > 1) || (v_major == 1 && v_minor > 4) || (v_major == 1 && v_minor == 4 && v_patch > 19), | ||
has_check: (v_major > 1) | ||
|| (v_major == 1 && v_minor > 4) | ||
|| (v_major == 1 && v_minor == 4 && v_patch > 10), | ||
has_wait: (v_major > 1) | ||
|| (v_major == 1 && v_minor > 4) | ||
|| (v_major == 1 && v_minor == 4 && v_patch > 19), | ||
}) | ||
} | ||
|
||
|
@@ -123,31 +137,35 @@ impl IPTables { | |
pub fn get_policy(&self, table: &str, chain: &str) -> IPTResult<String> { | ||
let builtin_chains = get_builtin_chains(table)?; | ||
if !builtin_chains.iter().as_slice().contains(&chain) { | ||
return Err(IPTError::Other("given chain is not a default chain in the given table, can't get policy")); | ||
return Err(IPTError::Other( | ||
"given chain is not a default chain in the given table, can't get policy", | ||
)); | ||
} | ||
|
||
let output = String::from_utf8_lossy(&self.run(&["-t", table, "-L", chain])?.stdout) | ||
.into_owned(); | ||
let output = | ||
String::from_utf8_lossy(&self.run(&["-t", table, "-L", chain])?.stdout).into_owned(); | ||
for item in output.trim().split("\n") { | ||
let fields = item.split(" ").collect::<Vec<&str>>(); | ||
if fields.len() > 1 && fields[0] == "Chain" && fields[1] == chain { | ||
return Ok(fields[3].replace(")", "")); | ||
} | ||
} | ||
Err(IPTError::Other("could not find the default policy for table and chain")) | ||
Err(IPTError::Other( | ||
"could not find the default policy for table and chain", | ||
)) | ||
} | ||
|
||
/// Set the default policy for a table/chain. | ||
pub fn set_policy(&self, table: &str, chain: &str, policy: &str) -> IPTResult<bool> { | ||
pub fn set_policy(&self, table: &str, chain: &str, policy: &str) -> IPTResult<()> { | ||
let builtin_chains = get_builtin_chains(table)?; | ||
if !builtin_chains.iter().as_slice().contains(&chain) { | ||
return Err(IPTError::Other("given chain is not a default chain in the given table, can't set policy")); | ||
return Err(IPTError::Other( | ||
"given chain is not a default chain in the given table, can't set policy", | ||
)); | ||
} | ||
|
||
match self.run(&["-t", table, "-P", chain, policy]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
self.run(&["-t", table, "-P", chain, policy]) | ||
.and_then(|_| Ok(())) | ||
} | ||
|
||
/// Executes a given `command` on the chain. | ||
|
@@ -165,7 +183,9 @@ impl IPTables { | |
} | ||
|
||
match self.run(&[&["-t", table, "-C", chain], rule.split_quoted().as_slice()].concat()) { | ||
Ok(output) => Ok(output.status.success()), | ||
Ok(_) => Ok(true), | ||
Err(IPTError::BadExitStatus(1)) => Ok(false), | ||
Err(IPTError::BadExitStatus(2)) => Ok(false), | ||
Err(err) => Err(err), | ||
} | ||
} | ||
|
@@ -175,61 +195,74 @@ impl IPTables { | |
#[cfg(target_os = "linux")] | ||
pub fn chain_exists(&self, table: &str, chain: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-L", chain]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Ok(_) => Ok(true), | ||
Err(IPTError::BadExitStatus(1)) => Ok(false), | ||
Err(err) => Err(err), | ||
} | ||
} | ||
|
||
/// Inserts `rule` in the `position` to the table/chain. | ||
/// Returns `true` if the rule is inserted. | ||
pub fn insert(&self, table: &str, chain: &str, rule: &str, position: i32) -> IPTResult<bool> { | ||
match self.run(&[&["-t", table, "-I", chain, &position.to_string()], rule.split_quoted().as_slice()].concat()) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn insert(&self, table: &str, chain: &str, rule: &str, position: i32) -> IPTResult<()> { | ||
self.run( | ||
&[ | ||
&["-t", table, "-I", chain, &position.to_string()], | ||
rule.split_quoted().as_slice(), | ||
] | ||
.concat(), | ||
) | ||
.and_then(|_| Ok(())) | ||
} | ||
|
||
/// Inserts `rule` in the `position` to the table/chain if it does not exist. | ||
/// Returns `true` if the rule is inserted. | ||
pub fn insert_unique(&self, table: &str, chain: &str, rule: &str, position: i32) -> IPTResult<bool> { | ||
pub fn insert_unique( | ||
&self, | ||
table: &str, | ||
chain: &str, | ||
rule: &str, | ||
position: i32, | ||
) -> IPTResult<()> { | ||
if self.exists(table, chain, rule)? { | ||
return Err(IPTError::Other("the rule exists in the table/chain")) | ||
return Err(IPTError::Other("the rule exists in the table/chain")); | ||
} | ||
|
||
self.insert(table, chain, rule, position) | ||
} | ||
|
||
/// Replaces `rule` in the `position` to the table/chain. | ||
/// Returns `true` if the rule is replaced. | ||
pub fn replace(&self, table: &str, chain: &str, rule: &str, position: i32) -> IPTResult<bool> { | ||
match self.run(&[&["-t", table, "-R", chain, &position.to_string()], rule.split_quoted().as_slice()].concat()) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn replace(&self, table: &str, chain: &str, rule: &str, position: i32) -> IPTResult<()> { | ||
self.run( | ||
&[ | ||
&["-t", table, "-R", chain, &position.to_string()], | ||
rule.split_quoted().as_slice(), | ||
] | ||
.concat(), | ||
) | ||
.and_then(|_| Ok(())) | ||
} | ||
|
||
/// Appends `rule` to the table/chain. | ||
/// Returns `true` if the rule is appended. | ||
pub fn append(&self, table: &str, chain: &str, rule: &str) -> IPTResult<bool> { | ||
match self.run(&[&["-t", table, "-A", chain], rule.split_quoted().as_slice()].concat()) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn append(&self, table: &str, chain: &str, rule: &str) -> IPTResult<()> { | ||
self.run(&[&["-t", table, "-A", chain], rule.split_quoted().as_slice()].concat()) | ||
.and_then(|_| Ok(())) | ||
} | ||
|
||
/// Appends `rule` to the table/chain if it does not exist. | ||
/// Returns `true` if the rule is appended. | ||
pub fn append_unique(&self, table: &str, chain: &str, rule: &str) -> IPTResult<bool> { | ||
pub fn append_unique(&self, table: &str, chain: &str, rule: &str) -> IPTResult<()> { | ||
if self.exists(table, chain, rule)? { | ||
return Err(IPTError::Other("the rule exists in the table/chain")) | ||
return Err(IPTError::Other("the rule exists in the table/chain")); | ||
} | ||
|
||
self.append(table, chain, rule) | ||
} | ||
|
||
/// Appends or replaces `rule` to the table/chain if it does not exist. | ||
/// Returns `true` if the rule is appended or replaced. | ||
pub fn append_replace(&self, table: &str, chain: &str, rule: &str) -> IPTResult<bool> { | ||
pub fn append_replace(&self, table: &str, chain: &str, rule: &str) -> IPTResult<()> { | ||
if self.exists(table, chain, rule)? { | ||
self.delete(table, chain, rule)?; | ||
} | ||
|
@@ -239,11 +272,9 @@ impl IPTables { | |
|
||
/// Deletes `rule` from the table/chain. | ||
/// Returns `true` if the rule is deleted. | ||
pub fn delete(&self, table: &str, chain: &str, rule: &str) -> IPTResult<bool> { | ||
match self.run(&[&["-t", table, "-D", chain], rule.split_quoted().as_slice()].concat()) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn delete(&self, table: &str, chain: &str, rule: &str) -> IPTResult<()> { | ||
self.run(&[&["-t", table, "-D", chain], rule.split_quoted().as_slice()].concat()) | ||
.and_then(|_| Ok(())) | ||
} | ||
|
||
/// Deletes all repetition of the `rule` from the table/chain. | ||
|
@@ -280,52 +311,40 @@ impl IPTables { | |
|
||
/// Creates a new user-defined chain. | ||
/// Returns `true` if the chain is created. | ||
pub fn new_chain(&self, table: &str, chain: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-N", chain]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn new_chain(&self, table: &str, chain: &str) -> IPTResult<()> { | ||
self.run(&["-t", table, "-N", chain]).and_then(|_| Ok(())) | ||
} | ||
|
||
/// Flushes (deletes all rules) a chain. | ||
/// Returns `true` if the chain is flushed. | ||
pub fn flush_chain(&self, table: &str, chain: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-F", chain]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn flush_chain(&self, table: &str, chain: &str) -> IPTResult<()> { | ||
self.run(&["-t", table, "-F", chain]).and_then(|_| Ok(())) | ||
} | ||
|
||
/// Renames a chain in the table. | ||
/// Returns `true` if the chain is renamed. | ||
pub fn rename_chain(&self, table: &str, old_chain: &str, new_chain: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-E", old_chain, new_chain]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn rename_chain(&self, table: &str, old_chain: &str, new_chain: &str) -> IPTResult<()> { | ||
self.run(&["-t", table, "-E", old_chain, new_chain]) | ||
.and_then(|_| Ok(())) | ||
} | ||
|
||
/// Deletes a user-defined chain in the table. | ||
/// Returns `true` if the chain is deleted. | ||
pub fn delete_chain(&self, table: &str, chain: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-X", chain]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn delete_chain(&self, table: &str, chain: &str) -> IPTResult<()> { | ||
self.run(&["-t", table, "-X", chain]).and_then(|_| Ok(())) | ||
} | ||
|
||
/// Flushes all chains in a table. | ||
/// Returns `true` if the chains are flushed. | ||
pub fn flush_table(&self, table: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-F"]) { | ||
Ok(output) => Ok(output.status.success()), | ||
Err(err) => Err(err), | ||
} | ||
pub fn flush_table(&self, table: &str) -> IPTResult<()> { | ||
self.run(&["-t", table, "-F"]).and_then(|_| Ok(())) | ||
} | ||
|
||
fn exists_old_version(&self, table: &str, chain: &str, rule: &str) -> IPTResult<bool> { | ||
match self.run(&["-t", table, "-S"]) { | ||
Ok(output) => Ok(String::from_utf8_lossy(&output.stdout).into_owned().contains(&format!("-A {} {}", chain, rule))), | ||
Ok(output) => Ok(String::from_utf8_lossy(&output.stdout) | ||
.into_owned() | ||
.contains(&format!("-A {} {}", chain, rule))), | ||
Err(err) => Err(err), | ||
} | ||
} | ||
|
@@ -352,14 +371,19 @@ impl IPTables { | |
|
||
let mut need_retry = true; | ||
while need_retry { | ||
match flock(file_lock.as_ref().unwrap().as_raw_fd(), FlockArg::LockExclusiveNonblock) { | ||
match flock( | ||
file_lock.as_ref().unwrap().as_raw_fd(), | ||
FlockArg::LockExclusiveNonblock, | ||
) { | ||
Ok(_) => need_retry = false, | ||
Err(e) => if e.errno() == nix::errno::EAGAIN { | ||
// FIXME: may cause infinite loop | ||
need_retry = true; | ||
} else { | ||
return Err(IPTError::Nix(e)); | ||
}, | ||
Err(e) => { | ||
if e.errno() == nix::errno::EAGAIN { | ||
// FIXME: may cause infinite loop | ||
need_retry = true; | ||
} else { | ||
return Err(IPTError::Nix(e)); | ||
} | ||
} | ||
} | ||
} | ||
output = output_cmd.args(args).output()?; | ||
|
@@ -372,6 +396,10 @@ impl IPTables { | |
}; | ||
} | ||
|
||
Ok(output) | ||
match output.status.code() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. output.status.code().and_then(|i| if i == 0 { Ok(output) } else { Err(IPTError::BadExitStatus(i)) }) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK this is invalid: |
||
None => Ok(output), | ||
Some(0) => Ok(output), | ||
Some(i) => Err(IPTError::BadExitStatus(i)), | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's an example where the result type should maybe be
IPResult<bool>
instead. Thoughts?