rust_rcs_core/ffi/
net_ctrl.rs

1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15extern crate libc;
16
17use std::{
18    ffi::{c_void, CStr},
19    net::SocketAddr,
20    ptr::NonNull,
21};
22
23#[cfg(any(
24    all(feature = "android", target_os = "android"),
25    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
26))]
27use libc::c_char;
28
29pub struct NetworkRequestCallbackWrapper {
30    callback: Option<Box<dyn FnOnce(bool) + Send + Sync + 'static>>,
31}
32
33#[cfg(any(
34    all(feature = "android", target_os = "android"),
35    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
36))]
37extern "C" fn network_activation_callback(ptr: *mut c_void, activated: bool) {
38    let data = ptr as *mut NetworkRequestCallbackWrapper;
39
40    unsafe {
41        if let Some(wrapper) = data.as_mut() {
42            if let Some(callback) = wrapper.callback.take() {
43                callback(activated);
44            }
45        }
46    }
47}
48
49#[repr(C)]
50pub struct NetworkRequestCHandle {
51    _data: [u8; 0],
52    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
53}
54
55pub struct NetworkRequestCHandleWrapper(NonNull<NetworkRequestCHandle>);
56
57impl Drop for NetworkRequestCHandleWrapper {
58    fn drop(&mut self) {
59        #[cfg(any(
60            all(feature = "android", target_os = "android"),
61            all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
62        ))]
63        let c_handle = self.0.as_ptr();
64        #[cfg(any(
65            all(feature = "android", target_os = "android"),
66            all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
67        ))]
68        unsafe {
69            platform_drop_network_request(c_handle);
70        }
71    }
72}
73
74unsafe impl Send for NetworkRequestCHandleWrapper {}
75
76#[repr(C)]
77pub struct NetworkInfoCHandle {
78    _data: [u8; 0],
79    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
80}
81
82pub struct NetworkInfoCHandleWrapper(NonNull<NetworkInfoCHandle>);
83
84impl Drop for NetworkInfoCHandleWrapper {
85    fn drop(&mut self) {
86        #[cfg(any(
87            all(feature = "android", target_os = "android"),
88            all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
89        ))]
90        let c_handle = self.0.as_ptr();
91        #[cfg(any(
92            all(feature = "android", target_os = "android"),
93            all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
94        ))]
95        unsafe {
96            platform_drop_network_info(c_handle);
97        }
98    }
99}
100
101unsafe impl Send for NetworkInfoCHandleWrapper {}
102
103#[repr(C)]
104pub struct DnsInfoCHandle {
105    _data: [u8; 0],
106    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
107}
108
109pub struct DnsInfoCHandleWrapper(NonNull<DnsInfoCHandle>);
110
111impl Drop for DnsInfoCHandleWrapper {
112    fn drop(&mut self) {
113        #[cfg(any(
114            all(feature = "android", target_os = "android"),
115            all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
116        ))]
117        let c_handle = self.0.as_ptr();
118        #[cfg(any(
119            all(feature = "android", target_os = "android"),
120            all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
121        ))]
122        unsafe {
123            platform_drop_dns_info(c_handle);
124        }
125    }
126}
127
128unsafe impl Send for DnsInfoCHandleWrapper {}
129
130#[cfg(any(
131    all(feature = "android", target_os = "android"),
132    all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
133))]
134extern "C" {
135    fn platform_activate_cellular_network(
136        ptr: *mut c_void,
137        callback: Option<extern "C" fn(*mut c_void, bool)>,
138    ) -> *mut NetworkRequestCHandle;
139    fn platform_drop_network_request(c_handle: *mut NetworkRequestCHandle);
140    fn platform_get_active_network_info() -> *mut NetworkInfoCHandle;
141    fn platform_get_network_type(network_info: *mut NetworkInfoCHandle) -> i32;
142    fn platform_get_network_dns_info(network_info: *mut NetworkInfoCHandle) -> *mut DnsInfoCHandle;
143    fn platform_get_dns_server(dns_info: *mut DnsInfoCHandle) -> *const c_char;
144    fn platform_drop_dns_info(dns_info: *mut DnsInfoCHandle);
145    fn platform_drop_network_info(network_info: *mut NetworkInfoCHandle);
146}
147
148pub fn activate_cellular_network<T>(callback: T) -> Option<NetworkRequestCHandleWrapper>
149where
150    T: 'static + FnOnce(bool) + Send + Sync,
151{
152    let wrapper = NetworkRequestCallbackWrapper {
153        callback: Some(Box::new(callback)),
154    };
155
156    let data = Box::into_raw(Box::new(wrapper));
157    let ptr = data as *mut c_void;
158
159    #[cfg(any(
160        all(feature = "android", target_os = "android"),
161        all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
162    ))]
163    unsafe {
164        if let Some(c_handle) =
165            platform_activate_cellular_network(ptr, Some(network_activation_callback)).as_mut()
166        {
167            return Some(NetworkRequestCHandleWrapper(
168                NonNull::new(c_handle).unwrap(),
169            ));
170        }
171    }
172
173    None
174}
175
176pub fn get_active_network_info() -> Option<NetworkInfoCHandleWrapper> {
177    #[cfg(any(
178        all(feature = "android", target_os = "android"),
179        all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
180    ))]
181    unsafe {
182        if let Some(network_info) = platform_get_active_network_info().as_mut() {
183            return Some(NetworkInfoCHandleWrapper(
184                NonNull::new(network_info).unwrap(),
185            ));
186        }
187    }
188
189    None
190}
191
192pub fn get_network_type(network_info: &NetworkInfoCHandleWrapper) -> i32 {
193    let network_info_c_handle = network_info.0.as_ptr();
194    #[cfg(any(
195        all(feature = "android", target_os = "android"),
196        all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
197    ))]
198    unsafe {
199        return platform_get_network_type(network_info_c_handle);
200    }
201
202    0
203}
204
205pub fn get_dns_info(network_info: &NetworkInfoCHandleWrapper) -> Option<DnsInfoCHandleWrapper> {
206    let network_info_c_handle = network_info.0.as_ptr();
207
208    #[cfg(any(
209        all(feature = "android", target_os = "android"),
210        all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
211    ))]
212    unsafe {
213        if let Some(dns_info) = platform_get_network_dns_info(network_info_c_handle).as_mut() {
214            return Some(DnsInfoCHandleWrapper(NonNull::new(dns_info).unwrap()));
215        }
216    }
217
218    None
219}
220
221pub fn get_dns_servers(dns_info: &DnsInfoCHandleWrapper) -> Vec<SocketAddr> {
222    let dns_info_c_handle = dns_info.0.as_ptr();
223
224    let mut v = Vec::new();
225
226    #[cfg(any(
227        all(feature = "android", target_os = "android"),
228        all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
229    ))]
230    unsafe {
231        while let Some(ptr) = platform_get_dns_server(dns_info_c_handle).as_ref() {
232            let str = CStr::from_ptr(ptr).to_string_lossy().into_owned();
233            if let Ok(dns_server) = str.parse::<SocketAddr>() {
234                v.push(dns_server);
235            }
236        }
237    }
238
239    v
240}
241
242pub fn get_active_dns_servers() -> Vec<SocketAddr> {
243    let mut v = Vec::new();
244
245    #[cfg(any(
246        all(feature = "android", target_os = "android"),
247        all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))
248    ))]
249    unsafe {
250        if let Some(network_info) = platform_get_active_network_info().as_mut() {
251            if let Some(dns_info) = platform_get_network_dns_info(network_info).as_mut() {
252                while let Some(ptr) = platform_get_dns_server(dns_info).as_ref() {
253                    let str = CStr::from_ptr(ptr).to_string_lossy().into_owned();
254                    if let Ok(dns_server) = str.parse::<SocketAddr>() {
255                        v.push(dns_server);
256                    }
257                }
258                platform_drop_dns_info(dns_info);
259            }
260        }
261    }
262
263    v
264}