Repository: orf/gping (language: python)

Issue:
Fails on FreeBSD 12.1
`thread '<unnamed>' panicked at 'Error pinging: Unsupported OS Unknown', /root/.cargo/registry/src/github.com-1ecc6299db9ec823/gping-0.1.6/src/main.rs:122:33`


Discussion / hints:
Can you run the following two commands and paste the output here:

* ping google.com
* ping ::1

I don't have a freebsd machine to test them on. ~5 lines each would be perfect.
No IPv6 here.

```
peter@peter-freebsd ~ % ping google.com
PING google.com (216.58.198.174): 56 data bytes
64 bytes from 216.58.198.174: icmp_seq=0 ttl=116 time=35.034 ms
64 bytes from 216.58.198.174: icmp_seq=1 ttl=116 time=35.266 ms
64 bytes from 216.58.198.174: icmp_seq=2 ttl=116 time=35.204 ms
64 bytes from 216.58.198.174: icmp_seq=3 ttl=116 time=34.941 ms
64 bytes from 216.58.198.174: icmp_seq=4 ttl=116 time=35.617 ms
^C
--- google.com ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 34.941/35.212/35.617/0.233 ms
peter@peter-freebsd ~ % ping ::1
ping: cannot resolve ::1: Unknown host
```
@orf: Can help test some stuff on FreeBSD too. 
dream:/tmp>ping google.com
PING google.com (142.250.180.206): 56 data bytes
64 bytes from 142.250.180.206: icmp_seq=0 ttl=120 time=14.247 ms
64 bytes from 142.250.180.206: icmp_seq=1 ttl=120 time=14.088 ms
64 bytes from 142.250.180.206: icmp_seq=2 ttl=120 time=14.097 ms
64 bytes from 142.250.180.206: icmp_seq=3 ttl=120 time=14.087 ms
^C
--- google.com ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 14.087/14.130/14.247/0.068 ms
dream:/tmp>ping ::1
PING6(56=40+8+8 bytes) ::1 --> ::1
16 bytes from ::1, icmp_seq=0 hlim=64 time=0.050 ms
16 bytes from ::1, icmp_seq=1 hlim=64 time=0.039 ms
16 bytes from ::1, icmp_seq=2 hlim=64 time=0.039 ms
16 bytes from ::1, icmp_seq=3 hlim=64 time=0.041 ms
16 bytes from ::1, icmp_seq=4 hlim=64 time=0.032 ms
^C
--- ::1 ping6 statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.032/0.040/0.050/0.006 ms
Had the same issue on Debian 11. It turned out that `ping` was not installed (actually gone for some reason after latest dist-upgrade). However it was hard to figure out what the issue was (for someone not expecting ping not to be there), maybe a graceful handling if ping is not present? Thanks for the `gping` tool, btw, it is great!
Same problem "Unsupported FreeBSD OS" on FreeBSD 14 (current).
Here's a patch for FreeBSD / OpenBSD / NetBSD / DragonFly.

The only problem is it requires running `gping` via `sudo`. I'm not sure why it doesn't work without sudo.

```
diff --git a/pinger/src/lib.rs b/pinger/src/lib.rs
index ec10ed7..4d97b01 100644
--- a/pinger/src/lib.rs
+++ b/pinger/src/lib.rs
@@ -30,6 +30,7 @@ extern crate lazy_static;
 pub mod linux;
 // pub mod alpine'
 pub mod macos;
+pub mod bsd;
 #[cfg(windows)]
 pub mod windows;

@@ -154,11 +155,17 @@ pub fn ping_with_interval(addr: String, interval: Duration) -> Result<mpsc::Rece
     }
     #[cfg(unix)]
     {
-        if cfg!(target_os = "macos") {
+        if cfg!(target_os = "freebsd") || cfg!(target_os = "dragonfly") || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd") {
+            let mut p = bsd::BSDPinger::default();
+            p.set_interval(interval);
+            p.start::<bsd::BSDParser>(addr)
+        }
+        else if cfg!(target_os = "macos") {
             let mut p = macos::MacOSPinger::default();
             p.set_interval(interval);
             p.start::<macos::MacOSParser>(addr)
-        } else {
+        }
+        else {
             match detect_linux_ping() {
                 Ok(LinuxPingType::IPTools) => {
                     let mut p = linux::LinuxPinger::default();
diff --git a/pinger/src/test.rs b/pinger/src/test.rs                                                                                                                                                                 
index 85d97f4..f752ff6 100644                                                                                                                                                                                        
--- a/pinger/src/test.rs                                                                                                                                                                                             
+++ b/pinger/src/test.rs                                                                                                                                                                                             
@@ -1,11 +1,12 @@                                                                                                                                                                                                    
 #[cfg(test)]                                                                                                                                                                                                        
 mod tests {                                                                                                                                                                                                         
+    use crate::{Parser, PingResult};                                                                                                                                                                                
     use crate::linux::LinuxParser;                                                                                                                                                                                  
-    // use crate::alpine::AlpineParser;                                                                                                                                                                             
     use crate::macos::MacOSParser;                                                                                                                                                                                  
+    use crate::bsd::BSDParser;                                                                                                                                                                                      
+                                                                                                                                                                                                                    
     #[cfg(windows)]                                                                                                                                                                                                 
     use crate::windows::WindowsParser;                                                                                                                                                                              
-    use crate::{Parser, PingResult};                                                                                                                                                                                
                                                                                                                                                                                                                     
     fn test_parser<T>(contents: &str)                                                                                                                                                                               
     where                                                                                                                                                                                                           
@@ -44,6 +45,26 @@ mod tests {                                                                                                                                                                                       
         test_parser::<MacOSParser>(include_str!("tests/macos.txt"));                                                                                                                                                
     }
  
+    #[test]
+    fn freebsd() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
+    #[test]
+    fn dragonfly() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
+    #[test]
+    fn openbsd() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
+    #[test]
+    fn netbsd() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
     #[test]
     fn ubuntu() {
         test_parser::<LinuxParser>(include_str!("tests/ubuntu.txt"));

```
@yonas
Is patch for 1.4.0 version?
Could you send a link or patch himself to my email, because thats a lot of garbage/spaces when trying to copy it.

Thanks
Happy to include this, but I think the patch is incomplete? Could you make a merge request, or push the changes to somewhere?
@orf
At https://people.freebsd.org/~eduardo/logs/gping/ we have a FreeBSD port skeleton and it builds ok and runs with a blank screen.

With patch applied I getting errors:
```
error[E0583]: file not found for module `bsd`
  --> pinger/src/lib.rs:33:1
   |
33 | pub mod bsd;
   | ^^^^^^^^^^^^
   |
   = help: to create the module `bsd`, create file "pinger/src/bsd.rs" or "pinger/src/bsd/mod.rs"

error[E0433]: failed to resolve: could not find `BSDPinger` in `bsd`
   --> pinger/src/lib.rs:159:30
    |
159 |             let mut p = bsd::BSDPinger::default();
    |                              ^^^^^^^^^ could not find `BSDPinger` in `bsd`

error[E0412]: cannot find type `BSDParser` in module `bsd`
   --> pinger/src/lib.rs:161:28
    |
161 |             p.start::<bsd::BSDParser>(addr)
    |                            ^^^^^^^^^ not found in `bsd`
```
full log: https://people.freebsd.org/~eduardo/logs/gping/gping-1.4.0.log

I did a manual cleanup on patch and it applies fine but I don't know if something is missing:
https://people.freebsd.org/~eduardo/logs/gping/full.patch
Sure, if you look at the patch @yonas posted it doesn't actually include the BSD specific code, hence the errors.
Oops, forgot `bsd.rs`:

```
use crate::{Parser, PingResult, Pinger};
use regex::Regex;
use std::time::Duration;

lazy_static! {
    static ref RE: Regex = Regex::new(r"time=(?:(?P<time>[0-9\.]+)\s+ms)").unwrap();
}

#[derive(Default)]
pub struct BSDPinger {
    interval: Duration,
}

impl Pinger for BSDPinger {
    fn set_interval(&mut self, interval: Duration) {
        self.interval = interval;
    }

    fn ping_args(&self, target: String) -> Vec<String> {
        vec![
            format!("-i{:.1}", self.interval.as_millis() as f32 / 1_000_f32),
            target,
        ]
    }
}

#[derive(Default)]
pub struct BSDParser {}

impl Parser for BSDParser {
    fn parse(&self, line: String) -> Option<PingResult> {
        if line.starts_with("PING ") {
            return None;
        }
        if line.starts_with("Request timeout") {
            return Some(PingResult::Timeout(line));
        }
        self.extract_regex(&RE, line)
    }
}
```
@nunotexbsd Yes, the patch is for v1.4.0.

Nice, it works ok like @yonas said: only with super user rights (su, sudo, doas, etc)

With a normal user, blanck screen and error:
`Error: receiving on a closed channel`

Any clues?
Ok, I just pulled @yonas patches so testing could be done in a better way.
(...) permissions

I've discouvered that ping have setuid bit:
```
In order for ping to work it needs to be able to create a raw network socket.
This is typically a privileged action.

On a modern Linux system this can be granted with "capabilities"

eg on CentOS 7:

$ ls -l /bin/ping
-rwxr-xr-x 1 root root 62088 Nov  7  2016 /bin/ping*

$ getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p
```
The same happens on bsd.
I've tried it with gping without success.
```
ls -l gping:
-r-sr-xr-x  1 root  wheel
```

Fix patch:
diff --git a/pinger/src/bsd.rs b/pinger/src/bsd.rs
new file mode 100644
index 000000000..66d464b1e
--- /dev/null
+++ b/pinger/src/bsd.rs
@@ -0,0 +1,40 @@
+use crate::{Parser, PingResult, Pinger};
+use regex::Regex;
+use std::time::Duration;
+
+lazy_static! {
+    static ref RE: Regex = Regex::new(r"time=(?:(?P<time>[0-9\.]+)\s+ms)").unwrap();
+}
+
+#[derive(Default)]
+pub struct BSDPinger {
+    interval: Duration,
+}
+
+impl Pinger for BSDPinger {
+    fn set_interval(&mut self, interval: Duration) {
+        self.interval = interval;
+    }
+
+    fn ping_args(&self, target: String) -> Vec<String> {
+        vec![
+            format!("-i{:.1}", self.interval.as_millis() as f32 / 1_000_f32),
+            target,
+        ]
+    }
+}
+
+#[derive(Default)]
+pub struct BSDParser {}
+
+impl Parser for BSDParser {
+    fn parse(&self, line: String) -> Option<PingResult> {
+        if line.starts_with("PING ") {
+            return None;
+        }
+        if line.starts_with("Request timeout") {
+            return Some(PingResult::Timeout(line));
+        }
+        self.extract_regex(&RE, line)
+    }
+}
diff --git a/pinger/src/lib.rs b/pinger/src/lib.rs
index ec10ed760..15a24ba4d 100644
--- a/pinger/src/lib.rs
+++ b/pinger/src/lib.rs
@@ -1,3 +1,4 @@
+#[cfg(unix)]
 use crate::linux::{detect_linux_ping, LinuxPingType, PingDetectionError};
 /// Pinger
 /// This crate exposes a simple function to ping remote hosts across different operating systems.
@@ -33,6 +34,7 @@ pub mod macos;
 #[cfg(windows)]
 pub mod windows;
 
+mod bsd;
 #[cfg(test)]
 mod test;
 
@@ -154,7 +156,15 @@ pub fn ping_with_interval(addr: String, interval: Duration) -> Result<mpsc::Rece
     }
     #[cfg(unix)]
     {
-        if cfg!(target_os = "macos") {
+        if cfg!(target_os = "freebsd")
+            || cfg!(target_os = "dragonfly")
+            || cfg!(target_os = "openbsd")
+            || cfg!(target_os = "netbsd")
+        {
+            let mut p = bsd::BSDPinger::default();
+            p.set_interval(interval);
+            p.start::<bsd::BSDParser>(addr)
+        } else if cfg!(target_os = "macos") {
             let mut p = macos::MacOSPinger::default();
             p.set_interval(interval);
             p.start::<macos::MacOSParser>(addr)


Test patch:
diff --git a/pinger/src/test.rs b/pinger/src/test.rs
index 85d97f4a9..a066fd9ce 100644
--- a/pinger/src/test.rs
+++ b/pinger/src/test.rs
@@ -1,11 +1,12 @@
 #[cfg(test)]
 mod tests {
+    use crate::bsd::BSDParser;
     use crate::linux::LinuxParser;
-    // use crate::alpine::AlpineParser;
     use crate::macos::MacOSParser;
+    use crate::{Parser, PingResult};
+
     #[cfg(windows)]
     use crate::windows::WindowsParser;
-    use crate::{Parser, PingResult};
 
     fn test_parser<T>(contents: &str)
     where
@@ -44,6 +45,26 @@ mod tests {
         test_parser::<MacOSParser>(include_str!("tests/macos.txt"));
     }
 
+    #[test]
+    fn freebsd() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
+    #[test]
+    fn dragonfly() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
+    #[test]
+    fn openbsd() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
+    #[test]
+    fn netbsd() {
+        test_parser::<BSDParser>(include_str!("tests/bsd.txt"));
+    }
+
     #[test]
     fn ubuntu() {
         test_parser::<LinuxParser>(include_str!("tests/ubuntu.txt"));
diff --git a/pinger/src/tests/bsd.txt b/pinger/src/tests/bsd.txt
new file mode 100644
index 000000000..d0987af89
--- /dev/null
+++ b/pinger/src/tests/bsd.txt
@@ -0,0 +1,13 @@
+PING google.com (216.58.198.174): 56 data bytes
+64 bytes from 96.47.72.84: icmp_seq=0 ttl=50 time=111.525 ms
+ping: sendto: Host is down
+64 bytes from 96.47.72.84: icmp_seq=1 ttl=50 time=110.395 ms
+ping: sendto: No route to host
+
+-----
+
+None
+111.525ms
+None
+110.395ms
+None