ExitPolicyRule

Struct ExitPolicyRule 

Source
pub struct ExitPolicyRule {
    pub is_accept: bool,
    pub min_port: u16,
    pub max_port: u16,
    /* private fields */
}
Expand description

A single rule in an exit policy.

Each rule specifies whether to accept or reject traffic to a particular address and port combination. Rules are evaluated in order, and the first matching rule determines whether traffic is allowed.

§Rule Format

Rules follow the Tor exit policy format:

accept|reject[6] addrspec:portspec

§Matching Semantics

A rule matches a destination if:

  1. The destination address matches the rule’s address specification (considering CIDR masks and address family)
  2. The destination port is within the rule’s port range

§Address Family Matching

  • Wildcard (*) rules match both IPv4 and IPv6 addresses
  • IPv4-specific rules (*4, explicit IPv4) only match IPv4 addresses
  • IPv6-specific rules (*6, explicit IPv6, accept6/reject6) only match IPv6

§Example

use stem_rs::exit_policy::ExitPolicyRule;
use std::net::IpAddr;

// Parse a rule that accepts HTTP traffic
let rule = ExitPolicyRule::parse("accept *:80").unwrap();
assert!(rule.is_accept);
assert!(rule.is_address_wildcard());
assert!(!rule.is_port_wildcard());

// Check if the rule matches a destination
let addr: IpAddr = "192.168.1.1".parse().unwrap();
assert!(rule.is_match(Some(addr), Some(80)));
assert!(!rule.is_match(Some(addr), Some(443)));

// Parse a CIDR rule
let rule = ExitPolicyRule::parse("reject 10.0.0.0/8:*").unwrap();
assert!(!rule.is_accept);
assert_eq!(rule.get_masked_bits(), Some(8));

§See Also

Fields§

§is_accept: bool

Whether this rule accepts (true) or rejects (false) matching traffic.

§min_port: u16

The minimum port number this rule applies to (inclusive).

§max_port: u16

The maximum port number this rule applies to (inclusive).

Implementations§

Source§

impl ExitPolicyRule

Source

pub fn parse(rule: &str) -> Result<Self, Error>

Parses an exit policy rule from a string.

The rule must follow the Tor exit policy format:

accept|reject[6] addrspec:portspec
§Arguments
  • rule - The rule string to parse
§Supported Formats
§Actions
  • accept - Allow matching traffic
  • reject - Block matching traffic
  • accept6 - Allow matching IPv6 traffic only
  • reject6 - Block matching IPv6 traffic only
§Address Specifications
  • * - Any address (IPv4 or IPv6)
  • *4 - Any IPv4 address
  • *6 - Any IPv6 address
  • A.B.C.D - Specific IPv4 address
  • A.B.C.D/N - IPv4 CIDR notation (N = 0-32)
  • A.B.C.D/M.M.M.M - IPv4 with subnet mask
  • [IPv6] - Specific IPv6 address
  • [IPv6]/N - IPv6 CIDR notation (N = 0-128)
§Port Specifications
  • * - Any port (1-65535)
  • N - Single port
  • N-M - Port range (inclusive)
§Errors

Returns Error::Parse if:

  • The rule doesn’t start with accept or reject
  • The address specification is invalid
  • The port specification is invalid
  • The CIDR mask is out of range
  • The port range is invalid (min > max)
§Example
use stem_rs::exit_policy::ExitPolicyRule;

// Various valid rule formats
let rule = ExitPolicyRule::parse("accept *:80").unwrap();
let rule = ExitPolicyRule::parse("reject 10.0.0.0/8:*").unwrap();
let rule = ExitPolicyRule::parse("accept 192.168.0.0/255.255.0.0:80-443").unwrap();
let rule = ExitPolicyRule::parse("accept6 [::1]:22").unwrap();
let rule = ExitPolicyRule::parse("reject [2001:db8::]/32:*").unwrap();

// Invalid rules
assert!(ExitPolicyRule::parse("allow *:80").is_err());  // Invalid action
assert!(ExitPolicyRule::parse("accept *").is_err());    // Missing port
assert!(ExitPolicyRule::parse("accept *:443-80").is_err()); // Invalid range
Source

pub fn is_match(&self, address: Option<IpAddr>, port: Option<u16>) -> bool

Checks if this rule matches a given destination.

A rule matches if both the address and port match the rule’s specifications. If either the address or port is None, the rule performs a “fuzzy” match where the missing component is considered to potentially match.

This is equivalent to calling is_match_strict with strict = false.

§Arguments
  • address - The destination IP address, or None to match any address
  • port - The destination port, or None to match any port
§Returns

true if the rule matches the destination, false otherwise.

§Matching Rules
  • If the rule has a wildcard address, any address matches
  • If the rule has a specific address/CIDR, only addresses in that range match
  • IPv4 rules don’t match IPv6 addresses and vice versa
  • If the rule has a wildcard port, any port matches
  • If the rule has a specific port range, only ports in that range match
§Example
use stem_rs::exit_policy::ExitPolicyRule;
use std::net::IpAddr;

let rule = ExitPolicyRule::parse("accept 10.0.0.0/8:80-443").unwrap();

// Exact match
let addr: IpAddr = "10.1.2.3".parse().unwrap();
assert!(rule.is_match(Some(addr), Some(80)));
assert!(rule.is_match(Some(addr), Some(443)));
assert!(!rule.is_match(Some(addr), Some(22)));

// Address outside CIDR range
let addr: IpAddr = "192.168.1.1".parse().unwrap();
assert!(!rule.is_match(Some(addr), Some(80)));

// Fuzzy match (None means "any")
assert!(rule.is_match(None, Some(80)));  // Any address, port 80
Source

pub fn is_match_strict( &self, address: Option<IpAddr>, port: Option<u16>, strict: bool, ) -> bool

Checks if this rule matches a destination with strict mode option.

Similar to is_match, but with control over how fuzzy matches (when address or port is None) are handled.

§Arguments
  • address - The destination IP address, or None to match any address
  • port - The destination port, or None to match any port
  • strict - Controls fuzzy match behavior:
    • false: Fuzzy matches return true for accept rules, false for reject
    • true: Fuzzy matches return false for accept rules, true for reject
§Strict Mode Semantics

When strict = true, the question becomes “does this rule match ALL possible values for the missing component?” rather than “does this rule match ANY possible value?”

This is useful for determining if traffic can definitely exit (strict=true) versus if traffic might be able to exit (strict=false).

§Example
use stem_rs::exit_policy::ExitPolicyRule;

let accept_rule = ExitPolicyRule::parse("accept 10.0.0.0/8:80").unwrap();

// Non-strict: "can ANY address on port 80 match?"
assert!(accept_rule.is_match_strict(None, Some(80), false));

// Strict: "do ALL addresses on port 80 match?"
assert!(!accept_rule.is_match_strict(None, Some(80), true));
Source

pub fn is_address_wildcard(&self) -> bool

Checks if this rule matches any address (is an address wildcard).

A rule is an address wildcard if its address specification is *, which matches both IPv4 and IPv6 addresses.

Note that *4 and *6 are NOT considered wildcards by this method, as they only match one address family.

§Returns

true if the rule matches any address, false otherwise.

§Example
use stem_rs::exit_policy::ExitPolicyRule;

assert!(ExitPolicyRule::parse("accept *:80").unwrap().is_address_wildcard());
assert!(!ExitPolicyRule::parse("accept *4:80").unwrap().is_address_wildcard());
assert!(!ExitPolicyRule::parse("accept *6:80").unwrap().is_address_wildcard());
assert!(!ExitPolicyRule::parse("accept 10.0.0.0/8:80").unwrap().is_address_wildcard());
Source

pub fn is_port_wildcard(&self) -> bool

Checks if this rule matches any port (is a port wildcard).

A rule is a port wildcard if its port specification covers all valid ports (1-65535), typically written as * in the rule string.

§Returns

true if the rule matches any port, false otherwise.

§Example
use stem_rs::exit_policy::ExitPolicyRule;

assert!(ExitPolicyRule::parse("accept *:*").unwrap().is_port_wildcard());
assert!(ExitPolicyRule::parse("accept *:1-65535").unwrap().is_port_wildcard());
assert!(!ExitPolicyRule::parse("accept *:80").unwrap().is_port_wildcard());
assert!(!ExitPolicyRule::parse("accept *:80-443").unwrap().is_port_wildcard());
Source

pub fn get_address_type(&self) -> AddressType

Returns the address type of this rule.

§Returns

The AddressType indicating whether this rule matches:

§Example
use stem_rs::exit_policy::{ExitPolicyRule, AddressType};

let rule = ExitPolicyRule::parse("accept *:80").unwrap();
assert_eq!(rule.get_address_type(), AddressType::Wildcard);

let rule = ExitPolicyRule::parse("accept 192.168.1.1:80").unwrap();
assert_eq!(rule.get_address_type(), AddressType::IPv4);

let rule = ExitPolicyRule::parse("accept [::1]:80").unwrap();
assert_eq!(rule.get_address_type(), AddressType::IPv6);
Source

pub fn get_mask(&self) -> Option<IpAddr>

Returns the subnet mask as an IP address.

For IPv4 rules, returns the mask in dotted-quad notation (e.g., 255.255.0.0). For IPv6 rules, returns the mask as an IPv6 address. For wildcard rules, returns None.

§Returns

The subnet mask as an IpAddr, or None for wildcard rules.

§Example
use stem_rs::exit_policy::ExitPolicyRule;
use std::net::IpAddr;

let rule = ExitPolicyRule::parse("accept 192.168.0.0/16:*").unwrap();
assert_eq!(rule.get_mask(), Some("255.255.0.0".parse::<IpAddr>().unwrap()));

let rule = ExitPolicyRule::parse("accept 10.0.0.0/8:*").unwrap();
assert_eq!(rule.get_mask(), Some("255.0.0.0".parse::<IpAddr>().unwrap()));

let rule = ExitPolicyRule::parse("accept *:80").unwrap();
assert_eq!(rule.get_mask(), None);
Source

pub fn get_masked_bits(&self) -> Option<u8>

Returns the number of bits in the subnet mask.

For CIDR notation like 10.0.0.0/8, this returns 8. For specific addresses without a mask, returns the full mask (32 for IPv4, 128 for IPv6). For wildcard rules, returns None.

§Returns

The number of mask bits, or None for wildcard rules.

§Example
use stem_rs::exit_policy::ExitPolicyRule;

let rule = ExitPolicyRule::parse("accept 10.0.0.0/8:*").unwrap();
assert_eq!(rule.get_masked_bits(), Some(8));

let rule = ExitPolicyRule::parse("accept 192.168.1.1:80").unwrap();
assert_eq!(rule.get_masked_bits(), Some(32));  // Full mask for specific address

let rule = ExitPolicyRule::parse("accept *:80").unwrap();
assert_eq!(rule.get_masked_bits(), None);
Source

pub fn is_default(&self) -> bool

Checks if this rule is part of Tor’s default exit policy suffix.

Tor appends a default policy suffix that blocks commonly abused ports (SMTP, NetBIOS, etc.) and then accepts all other traffic. This method returns true if this rule was identified as part of that suffix.

§Returns

true if this rule is part of the default policy suffix, false otherwise.

§See Also
Source

pub fn is_private(&self) -> bool

Checks if this rule was expanded from the private keyword.

The private keyword in Tor exit policies expands to rules blocking traffic to private/internal IP ranges (10.0.0.0/8, 192.168.0.0/16, 127.0.0.0/8, etc.). This method returns true if this rule was identified as part of that expansion.

§Returns

true if this rule was expanded from private, false otherwise.

§See Also
Source

pub fn address(&self) -> Option<IpAddr>

Returns the IP address this rule applies to.

For rules with a specific address or CIDR range, returns the base address. For wildcard rules, returns None.

§Returns

The IP address, or None for wildcard rules.

§Example
use stem_rs::exit_policy::ExitPolicyRule;
use std::net::IpAddr;

let rule = ExitPolicyRule::parse("accept 192.168.1.1:80").unwrap();
assert_eq!(rule.address(), Some("192.168.1.1".parse::<IpAddr>().unwrap()));

let rule = ExitPolicyRule::parse("accept 10.0.0.0/8:*").unwrap();
assert_eq!(rule.address(), Some("10.0.0.0".parse::<IpAddr>().unwrap()));

let rule = ExitPolicyRule::parse("accept *:80").unwrap();
assert_eq!(rule.address(), None);

Trait Implementations§

Source§

impl Clone for ExitPolicyRule

Source§

fn clone(&self) -> ExitPolicyRule

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ExitPolicyRule

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ExitPolicyRule

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromStr for ExitPolicyRule

Source§

type Err = Error

The associated error which can be returned from parsing.
Source§

fn from_str(s: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more
Source§

impl PartialEq for ExitPolicyRule

Source§

fn eq(&self, other: &ExitPolicyRule) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for ExitPolicyRule

Source§

impl StructuralPartialEq for ExitPolicyRule

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.