From f12ae3b8f7e08c021276340e24f5082053f645ad Mon Sep 17 00:00:00 2001
From: richerfu <southorange0929@foxmail.com>
Date: Tue, 19 Nov 2024 16:35:47 +0800
Subject: [PATCH] feat: support openharmony platform

---
 README.md                    |   1 +
 examples/dev-config.rs       |   2 +-
 examples/ping-tun.rs         |   2 +-
 examples/read-async-codec.rs |   2 +-
 examples/read-async.rs       |   2 +-
 examples/read.rs             |   2 +-
 examples/split.rs            |   2 +-
 examples/write.rs            |   2 +-
 src/platform/mod.rs          |   9 +-
 src/platform/ohos/device.rs  | 174 +++++++++++++++++++++++++++++++++++
 src/platform/ohos/mod.rs     |  30 ++++++
 11 files changed, 219 insertions(+), 9 deletions(-)
 create mode 100644 src/platform/ohos/device.rs
 create mode 100644 src/platform/ohos/mod.rs

diff --git a/README.md b/README.md
index 2a9de1be..b6dc7dc1 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ Platforms
 - [x] FreeBSD
 - [x] Android
 - [x] iOS
+- [x] OpenHarmony
 
 
 Linux
diff --git a/examples/dev-config.rs b/examples/dev-config.rs
index bd9fa153..f36c3c1c 100644
--- a/examples/dev-config.rs
+++ b/examples/dev-config.rs
@@ -41,7 +41,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> {
         .destination((10, 0, 0, 1))
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         config.ensure_root_privileges(true);
     });
diff --git a/examples/ping-tun.rs b/examples/ping-tun.rs
index 2e13ef57..bdf54e32 100644
--- a/examples/ping-tun.rs
+++ b/examples/ping-tun.rs
@@ -40,7 +40,7 @@ async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> {
         .destination((10, 0, 0, 1))
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         #[allow(deprecated)]
         config.packet_information(true);
diff --git a/examples/read-async-codec.rs b/examples/read-async-codec.rs
index a0d16d10..976dd867 100644
--- a/examples/read-async-codec.rs
+++ b/examples/read-async-codec.rs
@@ -64,7 +64,7 @@ async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> {
         .destination((10, 0, 0, 1))
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         #[allow(deprecated)]
         config.packet_information(true);
diff --git a/examples/read-async.rs b/examples/read-async.rs
index 62d52bf6..c5dcc5e1 100644
--- a/examples/read-async.rs
+++ b/examples/read-async.rs
@@ -40,7 +40,7 @@ async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> {
         .mtu(tun::DEFAULT_MTU)
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         config.ensure_root_privileges(true);
     });
diff --git a/examples/read.rs b/examples/read.rs
index 377f2ecd..97f5de12 100644
--- a/examples/read.rs
+++ b/examples/read.rs
@@ -40,7 +40,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> {
         .destination((10, 0, 0, 1))
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         config.ensure_root_privileges(true);
     });
diff --git a/examples/split.rs b/examples/split.rs
index 07774ff5..f32d69ba 100644
--- a/examples/split.rs
+++ b/examples/split.rs
@@ -41,7 +41,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> {
         .destination((10, 0, 0, 1))
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         config.ensure_root_privileges(true);
     });
diff --git a/examples/write.rs b/examples/write.rs
index ed4245a6..3d619713 100644
--- a/examples/write.rs
+++ b/examples/write.rs
@@ -41,7 +41,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> {
         .destination((10, 0, 0, 1))
         .up();
 
-    #[cfg(target_os = "linux")]
+    #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
     config.platform_config(|config| {
         config.ensure_root_privileges(true);
     });
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index 9973d928..a699a72a 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -17,9 +17,9 @@
 #[cfg(unix)]
 pub(crate) mod posix;
 
-#[cfg(target_os = "linux")]
+#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
 pub(crate) mod linux;
-#[cfg(target_os = "linux")]
+#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
 pub use self::linux::{create, Device, PlatformConfig};
 
 #[cfg(target_os = "freebsd")]
@@ -42,6 +42,11 @@ pub(crate) mod android;
 #[cfg(target_os = "android")]
 pub use self::android::{create, Device, PlatformConfig};
 
+#[cfg(target_env = "ohos")]
+pub(crate) mod ohos;
+#[cfg(target_env = "ohos")]
+pub use self::ohos::{create, Device, PlatformConfig};
+
 #[cfg(unix)]
 pub use crate::platform::posix::Tun;
 
diff --git a/src/platform/ohos/device.rs b/src/platform/ohos/device.rs
new file mode 100644
index 00000000..3e703eed
--- /dev/null
+++ b/src/platform/ohos/device.rs
@@ -0,0 +1,174 @@
+//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+//                    Version 2, December 2004
+//
+// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
+//
+// Everyone is permitted to copy and distribute verbatim or modified
+// copies of this license document, and changing it is allowed as long
+// as the name is changed.
+//
+//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+//   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+//
+//  0. You just DO WHAT THE FUCK YOU WANT TO.
+#![allow(unused_variables)]
+
+use std::io::{Read, Write};
+use std::net::IpAddr;
+use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
+
+use crate::configuration::Configuration;
+use crate::device::AbstractDevice;
+use crate::error::{Error, Result};
+use crate::platform::posix::{self, Fd, Tun};
+
+/// A TUN device for Android.
+pub struct Device {
+    tun: Tun,
+}
+
+impl AsRef<dyn AbstractDevice + 'static> for Device {
+    fn as_ref(&self) -> &(dyn AbstractDevice + 'static) {
+        self
+    }
+}
+
+impl AsMut<dyn AbstractDevice + 'static> for Device {
+    fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) {
+        self
+    }
+}
+
+impl Device {
+    /// Create a new `Device` for the given `Configuration`.
+    pub fn new(config: &Configuration) -> Result<Self> {
+        let close_fd_on_drop = config.close_fd_on_drop.unwrap_or(true);
+        let fd = match config.raw_fd {
+            Some(raw_fd) => raw_fd,
+            _ => return Err(Error::InvalidConfig),
+        };
+        let device = {
+            let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU);
+            let tun = Fd::new(fd, close_fd_on_drop).map_err(|_| std::io::Error::last_os_error())?;
+
+            Device {
+                tun: Tun::new(tun, mtu, false),
+            }
+        };
+
+        Ok(device)
+    }
+
+    /// Split the interface into a `Reader` and `Writer`.
+    pub fn split(self) -> (posix::Reader, posix::Writer) {
+        (self.tun.reader, self.tun.writer)
+    }
+
+    /// Set non-blocking mode
+    pub fn set_nonblock(&self) -> std::io::Result<()> {
+        self.tun.set_nonblock()
+    }
+
+    /// Recv a packet from tun device
+    pub fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.tun.recv(buf)
+    }
+
+    /// Send a packet to tun device
+    pub fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
+        self.tun.send(buf)
+    }
+}
+
+impl Read for Device {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.tun.read(buf)
+    }
+}
+
+impl Write for Device {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.tun.write(buf)
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.tun.flush()
+    }
+}
+
+impl AbstractDevice for Device {
+    fn tun_index(&self) -> Result<i32> {
+        Err(Error::NotImplemented)
+    }
+
+    fn tun_name(&self) -> Result<String> {
+        Ok("".to_string())
+    }
+
+    fn set_tun_name(&mut self, value: &str) -> Result<()> {
+        Err(Error::NotImplemented)
+    }
+
+    fn enabled(&mut self, value: bool) -> Result<()> {
+        Ok(())
+    }
+
+    fn address(&self) -> Result<IpAddr> {
+        Err(Error::NotImplemented)
+    }
+
+    fn set_address(&mut self, _value: IpAddr) -> Result<()> {
+        Ok(())
+    }
+
+    fn destination(&self) -> Result<IpAddr> {
+        Err(Error::NotImplemented)
+    }
+
+    fn set_destination(&mut self, _value: IpAddr) -> Result<()> {
+        Ok(())
+    }
+
+    fn broadcast(&self) -> Result<IpAddr> {
+        Err(Error::NotImplemented)
+    }
+
+    fn set_broadcast(&mut self, _value: IpAddr) -> Result<()> {
+        Ok(())
+    }
+
+    fn netmask(&self) -> Result<IpAddr> {
+        Err(Error::NotImplemented)
+    }
+
+    fn set_netmask(&mut self, _value: IpAddr) -> Result<()> {
+        Ok(())
+    }
+
+    fn mtu(&self) -> Result<u16> {
+        // TODO: must get the mtu from the underlying device driver
+        Ok(self.tun.mtu())
+    }
+
+    fn set_mtu(&mut self, value: u16) -> Result<()> {
+        // TODO: must set the mtu to the underlying device driver
+        self.tun.set_mtu(value);
+        Ok(())
+    }
+
+    fn packet_information(&self) -> bool {
+        self.tun.packet_information()
+    }
+}
+
+impl AsRawFd for Device {
+    fn as_raw_fd(&self) -> RawFd {
+        self.tun.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for Device {
+    fn into_raw_fd(self) -> RawFd {
+        self.tun.into_raw_fd()
+    }
+}
diff --git a/src/platform/ohos/mod.rs b/src/platform/ohos/mod.rs
new file mode 100644
index 00000000..b6ab7167
--- /dev/null
+++ b/src/platform/ohos/mod.rs
@@ -0,0 +1,30 @@
+//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+//                    Version 2, December 2004
+//
+// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
+//
+// Everyone is permitted to copy and distribute verbatim or modified
+// copies of this license document, and changing it is allowed as long
+// as the name is changed.
+//
+//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+//   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+//
+//  0. You just DO WHAT THE FUCK YOU WANT TO.
+
+//! OpenHarmony specific functionality.
+
+mod device;
+pub use self::device::Device;
+
+use crate::configuration::Configuration;
+use crate::error::Result;
+
+/// Android-only interface configuration.
+#[derive(Copy, Clone, Default, Debug)]
+pub struct PlatformConfig;
+
+/// Create a TUN device with the given name.
+pub fn create(configuration: &Configuration) -> Result<Device> {
+    Device::new(configuration)
+}