Skip to content
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

Fallback #2

Merged
merged 4 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
on: [push, pull_request]

name: if-watch

jobs:
ci:
strategy:
fail-fast: false
matrix:
toolchain:
- rust: stable
#- rust: nightly
platform:
- target: x86_64-unknown-linux-gnu
host: ubuntu-latest
cross: false

- target: x86_64-apple-darwin
host: macos-latest
cross: false

- target: x86_64-pc-windows-msvc
host: windows-latest
cross: false

- target: armv7-linux-androideabi
host: ubuntu-latest
cross: true
- target: aarch64-linux-android
host: ubuntu-latest
cross: true

- target: aarch64-apple-ios
host: macos-latest
cross: true
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
LLVM_CONFIG_PATH: /usr/local/opt/llvm/bin/llvm-config
NDK_HOME: /usr/local/lib/android/sdk/ndk-bundle

runs-on: ${{ matrix.platform.host }}
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Cache cargo folder
uses: actions/cache@v1
with:
path: ~/.cargo
key: ${{ matrix.platform.target }}-cargo-${{ matrix.toolchain.rust }}

- name: Install dependencies ubuntu
if: matrix.platform.host == 'ubuntu-latest'
run: sudo apt-get install llvm-dev

- name: Install dependencies macos
if: matrix.platform.host == 'macos-latest'
run: brew install llvm

- name: Install dependencies windows
if: matrix.platform.host == 'windows-latest'
run: choco install llvm

- name: Install rust toolchain
uses: hecrj/setup-rust-action@v1
with:
rust-version: ${{ matrix.toolchain.rust }}
targets: ${{ matrix.platform.target }}

- name: Install cargo-ndk
if: contains(matrix.platform.target, 'android')
run: cargo install cargo-ndk

- name: Build
if: contains(matrix.platform.target, 'android') == false
run: cargo build --workspace --all-features --target ${{ matrix.platform.target }}

- name: Build android
if: contains(matrix.platform.target, 'android')
run: cargo ndk --android-platform 29 --target ${{ matrix.platform.target }} build --workspace --all-features

- name: Rust tests
if: matrix.platform.cross == false
run: cargo test --workspace --all-features

lint-rust:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Cache cargo folder
uses: actions/cache@v1
with:
path: ~/.cargo
key: lint-cargo

- name: Install rust toolchain
uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
components: clippy, rustfmt

- name: cargo fmt
run: cargo fmt --all -- --check

- name: cargo clippy
run: cargo clippy --workspace --all-features --examples --tests -- -D warnings
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ repository = "https://github.com/dvc94ch/if-watch"
[dependencies]
futures-lite = "1.11.2"
ipnet = "2.3.0"
libc = "0.2.66"
libc = "0.2.80"
log = "0.4.11"

[target.'cfg(unix)'.dependencies]
async-io = "1.2.0"
[target.'cfg(not(windows))'.dependencies]
async-io = "1.3.0"

[target.'cfg(not(target_os = "linux"))'.dependencies]
if-addrs = "0.6.5"

[target.'cfg(windows)'.dependencies]
futures = { version = "0.3.8", default-features = false }
if-addrs = "0.6.5"
winapi = { version = "0.3.8", features = ["netioapi", "ntdef", "winerror", "ws2def"] }
winapi = { version = "0.3.9", features = ["netioapi", "ntdef", "winerror", "ws2def"] }

[dev-dependencies]
env_logger = "0.8.1"
env_logger = "0.8.2"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Got event Ok(Up(fe80::cef9:e4ff:fe9e:b23b/64))
```

Supported platforms at the moment are:
Linux, Windows and Android
Linux, Windows and Android with a fallback for Macos and ios that polls for changes every 10s.

## License
MIT OR Apache-2.0
82 changes: 82 additions & 0 deletions src/fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::IfEvent;
use async_io::Timer;
use futures_lite::StreamExt;
use if_addrs::IfAddr;
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use std::io::Result;
use std::{
collections::{HashSet, VecDeque},
time::{Duration, Instant},
};

/// An address set/watcher
#[derive(Debug)]
pub struct IfWatcher {
addrs: HashSet<IpNet>,
queue: VecDeque<IfEvent>,
ticker: Timer,
}

impl IfWatcher {
/// Create a watcher
pub async fn new() -> Result<Self> {
Ok(Self {
addrs: Default::default(),
queue: Default::default(),
ticker: Timer::interval_at(Instant::now(), Duration::from_secs(10)),
})
}

fn resync(&mut self) -> Result<()> {
let addrs = if_addrs::get_if_addrs()?;
for old_addr in self.addrs.clone() {
if addrs
.iter()
.find(|addr| addr.ip() == old_addr.addr())
.is_none()
{
self.addrs.remove(&old_addr);
self.queue.push_back(IfEvent::Down(old_addr));
}
}
for new_addr in addrs {
let ipnet = ifaddr_to_ipnet(new_addr.addr);
if self.addrs.insert(ipnet) {
self.queue.push_back(IfEvent::Up(ipnet));
}
}
Ok(())
}

pub fn iter(&self) -> impl Iterator<Item = &IpNet> {
self.addrs.iter()
}

/// Returns a future for the next event.
pub async fn next(&mut self) -> Result<IfEvent> {
loop {
if let Some(event) = self.queue.pop_front() {
return Ok(event);
}
self.ticker.next().await;
self.resync()?;
}
}
}

fn ifaddr_to_ipnet(addr: IfAddr) -> IpNet {
match addr {
IfAddr::V4(ip) => {
let prefix_len = (!u32::from_be_bytes(ip.netmask.octets())).leading_zeros();
IpNet::V4(
Ipv4Net::new(ip.ip, prefix_len as u8).expect("if_addrs returned a valid prefix"),
)
}
IfAddr::V6(ip) => {
let prefix_len = (!u128::from_be_bytes(ip.netmask.octets())).leading_zeros();
IpNet::V6(
Ipv6Net::new(ip.ip, prefix_len as u8).expect("if_addrs returned a valid prefix"),
)
}
}
}
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ use std::io::Result;
#[cfg(not(any(unix, windows)))]
compile_error!("Only Unix and Windows are supported");

#[cfg(unix)]
#[cfg(not(any(target_os = "linux", windows)))]
mod fallback;
#[cfg(target_os = "linux")]
mod unix;
#[cfg(windows)]
mod windows;

#[cfg(unix)]
#[cfg(not(any(target_os = "linux", windows)))]
use fallback as platform_impl;
#[cfg(target_os = "linux")]
use unix as platform_impl;
#[cfg(windows)]
use windows as platform_impl;
Expand Down