cursock/
adapter.rs

1#[cfg(target_os = "linux")]
2use std::ffi::CString;
3use std::io;
4use std::net;
5
6use crate::*;
7
8pub struct Adapter {
9    #[cfg(target_os = "windows")]
10    guid: String,
11    #[cfg(target_os = "linux")]
12    index: i32,
13    name: String,
14    ipv4: Option<net::Ipv4Addr>,
15    ipv6: Option<net::Ipv6Addr>,
16    gateway: Option<net::Ipv4Addr>,
17    mac: Mac,
18}
19
20impl Adapter {
21    /// initializes struct using interface id
22    ///
23    /// # Examples
24    /// ```
25    /// let interface = Adapter::get_by_id(10, IpVer::V4).expect("error finding adapter");
26    /// ```
27    #[cfg(target_os = "windows")]
28    pub fn get_by_id(id: u32) -> io::Result<Self> {
29        let (ipv4, ipv6, gateway, mac, guid, name) = get_interface_info(id)?;
30
31        Ok(Self {
32            name,
33            ipv4,
34            ipv6,
35            gateway,
36            mac,
37            guid,
38        })
39    }
40
41    /// initializes struct using interface name
42    ///
43    /// # Examples
44    /// ```
45    /// let adapter = Adapter::get_by_ifname("wlan0", IpVer::V4).expect("error finding adapter");
46    ///
47    /// ```
48    #[cfg(target_os = "linux")]
49    pub fn get_by_ifname(if_name: &str) -> io::Result<Self> {
50        let cstr_if_name = CString::new(if_name)
51            .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))?;
52
53        let (index, ipv4, ipv6, mac) = get_interface_info(cstr_if_name)?;
54        let gateway = get_file_default_gateway();
55
56        Ok(Self {
57            index,
58            name: if_name.to_string(),
59            ipv4,
60            ipv6,
61            gateway,
62            mac,
63        })
64    }
65
66    getters!(
67        pub get_ipv4(ipv4) -> Option<net::Ipv4Addr>;
68        pub get_ipv6(ipv6) -> Option<net::Ipv6Addr>;
69        pub get_gateway(gateway) -> Option<net::Ipv4Addr>;
70        pub get_mac(mac) -> Mac;
71        pub get_name(name) -> str;
72    );
73
74    #[cfg(target_os = "windows")]
75    getters!(
76        pub get_guid(guid) -> str;
77    );
78
79    #[cfg(target_os = "linux")]
80    getters!(
81        pub get_index(index) -> i32;
82    );
83}
84
85impl ToString for Adapter {
86    fn to_string(&self) -> String {
87        let ip = self
88            .ipv4
89            .map(net::IpAddr::V4)
90            .or_else(|| self.ipv6.map(net::IpAddr::V6));
91
92        format!(
93            "{} ({} - {:?} - {:?})",
94            self.name, self.mac, ip, self.gateway
95        )
96    }
97}
98
99impl Clone for Adapter {
100    #[cfg(target_os = "windows")]
101    fn clone(&self) -> Self {
102        Self {
103            name: self.name.clone(),
104            ipv4: self.ipv4,
105            ipv6: self.ipv6,
106            gateway: self.gateway,
107            mac: self.mac.clone(),
108            guid: self.guid.clone(),
109        }
110    }
111
112    #[cfg(target_os = "linux")]
113    fn clone(&self) -> Self {
114        Self {
115            name: self.name.clone(),
116            ipv4: self.ipv4,
117            ipv6: self.ipv6,
118            gateway: self.gateway,
119            mac: self.mac.clone(),
120            index: self.index,
121        }
122    }
123
124    #[cfg(not(any(target_os = "windows", target_os = "linux")))]
125    fn clone(&self) -> Self {
126        Self {
127            name: self.name.clone(),
128            ipv4: self.ipv4.clone(),
129            ipv6: self.ipv6.clone(),
130            gateway: self.gateway.clone(),
131            mac: self.mac.clone(),
132        }
133    }
134}
135
136#[cfg(target_os = "linux")]
137fn get_interface_info(
138    if_name: CString,
139) -> io::Result<(i32, Option<net::Ipv4Addr>, Option<net::Ipv6Addr>, Mac)> {
140    let socketv4 = unsafe { ccs::socket(ccs::AF_INET, ccs::SOCK_DGRAM, 0) };
141    if socketv4 < 0 {
142        return Err(io::Error::last_os_error());
143    }
144
145    let ifru: ccs::ifreq_data = ccs::ifreq_data { ifru_ifindex: 0 };
146    let mut if_request: ccs::ifreq = ccs::ifreq {
147        ifr_name: [0; 16],
148        ifr_ifru: ifru,
149    };
150
151    memcpy(
152        if_request.ifr_name.as_mut_ptr(),
153        if_name.as_ptr(),
154        if_name.as_bytes_with_nul().len(),
155    );
156
157    let ifindex: i32 = get_if_index(socketv4, &mut if_request)?;
158
159    let ipv4 = get_if_ipv4(socketv4, &mut if_request).ok();
160
161    let socketv6 = unsafe { ccs::socket(ccs::AF_INET6, ccs::SOCK_DGRAM, 0) };
162    if socketv6 < 0 {
163        return Err(io::Error::last_os_error());
164    }
165
166    let ipv6 = get_if_ipv6(socketv6, &mut if_request).ok();
167
168    let mac: Mac = get_if_mac(socketv4, &mut if_request)?;
169
170    Ok((ifindex, ipv4, ipv6, mac))
171}
172
173#[cfg(target_os = "linux")]
174fn get_if_index(socket: i32, ifr: *mut ccs::ifreq) -> io::Result<i32> {
175    let err: i32 = unsafe { ccs::ioctl(socket, ccs::SIOCGIFINDEX, ifr) };
176    if err == -1 {
177        return Err(io::Error::last_os_error());
178    }
179
180    let index: i32 = unsafe { (*ifr).ifr_ifru.ifru_ifindex };
181
182    Ok(index)
183}
184
185#[cfg(target_os = "linux")]
186fn get_if_ipv4(socket: i32, ifr: *mut ccs::ifreq) -> io::Result<net::Ipv4Addr> {
187    let err: i32 = unsafe { ccs::ioctl(socket, ccs::SIOCGIFADDR, ifr) };
188
189    if err == -1 {
190        return Err(io::Error::last_os_error());
191    }
192
193    let addr: *const ccs::sockaddr_in =
194        unsafe { &(*ifr).ifr_ifru.ifru_addr as *const ccs::sockaddr } as *const ccs::sockaddr_in;
195
196    Ok(net::Ipv4Addr::from(unsafe { (*addr).sin_addr.s_addr.to_ne_bytes() }))
197}
198
199#[cfg(target_os = "linux")]
200fn get_if_ipv6(socket: i32, ifr: *mut ccs::ifreq) -> io::Result<net::Ipv6Addr> {
201    let err: i32 = unsafe { ccs::ioctl(socket, ccs::SIOCGIFADDR, ifr) };
202
203    if err == -1 {
204        return Err(io::Error::last_os_error());
205    }
206
207    let addr = unsafe {
208        (*(&(*ifr).ifr_ifru.ifru_addr as *const _ as *const ccs::sockaddr_in6))
209            .sin6_addr
210            .s6_addr
211    };
212
213    Ok(net::Ipv6Addr::from(addr))
214}
215
216#[cfg(target_os = "linux")]
217fn get_if_mac(socket: i32, ifr: *mut ccs::ifreq) -> io::Result<Mac> {
218    let err: i32 = unsafe { ccs::ioctl(socket, ccs::SIOCGIFHWADDR, ifr) };
219
220    if err == -1 {
221        return Err(io::Error::last_os_error());
222    }
223
224    let sa_data: [i8; 14] = unsafe { (*ifr).ifr_ifru.ifru_hwaddr.sa_data };
225
226    let mut mac: [u8; MAC_LEN] = [0; MAC_LEN];
227
228    memcpy(mac.as_mut_ptr(), sa_data.as_ptr(), MAC_LEN);
229
230    Ok(Mac::from(mac))
231}
232
233#[cfg(target_os = "linux")]
234fn get_file_default_gateway() -> Option<net::Ipv4Addr> {
235    use std::{fs, io::BufRead};
236
237    let file = fs::File::open("/proc/net/route").ok()?;
238    let reader = io::BufReader::new(file);
239
240    for line in reader.lines().flatten() {
241        let mut fields = line.split('\t');
242        let interface = fields.next();
243        let destination = fields.next();
244        let gateway = fields.next();
245
246        if let (Some(_), Some(destination), Some(gateway)) = (interface, destination, gateway) {
247            if destination == "00000000" {
248                let gateway_ip = u32::from_str_radix(gateway, 16).ok()?;
249
250                return Some(net::Ipv4Addr::from(gateway_ip.to_ne_bytes()));
251            }
252        }
253    }
254
255    None
256}
257
258#[cfg(target_os = "windows")]
259fn get_interface_info(
260    if_id: u32,
261) -> io::Result<(
262    Option<net::Ipv4Addr>,
263    Option<net::Ipv6Addr>,
264    Option<net::Ipv4Addr>,
265    Mac,
266    String,
267    String,
268)> {
269    use crate::ccs::AF_INET;
270
271    let mut out_buf_len: u32 = 0;
272    let flags = ccs::GAA_FLAG_INCLUDE_GATEWAYS;
273
274    unsafe {
275        ccs::GetAdaptersAddresses(
276            ccs::AF_UNSPEC as u32,
277            flags as u32,
278            std::ptr::null_mut(),
279            std::ptr::null_mut(),
280            &mut out_buf_len,
281        )
282    };
283
284    let buffer_size = out_buf_len;
285    let mut buffer: Vec<u8> = vec![0; buffer_size as usize];
286    let addresses = buffer.as_mut_ptr() as *mut ccs::IP_ADAPTER_ADDRESSES;
287
288    let result = unsafe {
289        ccs::GetAdaptersAddresses(
290            ccs::AF_UNSPEC as u32,
291            flags as u32,
292            std::ptr::null_mut(),
293            addresses,
294            &mut out_buf_len,
295        )
296    };
297
298    if result != 0 {
299        return Err(io::Error::new(
300            io::ErrorKind::ConnectionReset,
301            format!("unknown error occurred with {} error code, while running GetAdaptersAddresses ex call", result)
302        ));
303    }
304
305    let mut output = None;
306
307    let mut cur_addr = addresses;
308    while !cur_addr.is_null() {
309        let cur_addr_r = unsafe { &mut *cur_addr };
310
311        if cur_addr_r.if_index == if_id {
312            let mut ipv4 = None;
313            let mut ipv6 = None;
314            let mut gateway_ip = None;
315            let mut mac = [0; MAC_LEN];
316            memcpy(
317                mac.as_mut_ptr(),
318                cur_addr_r.physical_address.as_ptr(),
319                MAC_LEN,
320            );
321
322            let mac = Mac::from(mac);
323
324            let guid = str_from_cstr(cur_addr_r.adapter_name as *const i8);
325            let slice = unsafe {
326                std::slice::from_raw_parts(cur_addr_r.friendly_name, {
327                    let mut len = 0;
328                    while *cur_addr_r.friendly_name.add(len) != 0 {
329                        len += 1;
330                    }
331                    len
332                })
333            };
334            let name = slice
335                .iter()
336                .map(|u| std::char::from_u32(*u as u32).unwrap())
337                .collect::<String>();
338
339            let mut gateway = cur_addr_r.first_gateway_address;
340            while !gateway.is_null() {
341                let r_gateway = unsafe { &mut *gateway };
342
343                let sockaddr =
344                    unsafe { &*(r_gateway.address.lp_sockaddr as *const ccs::sockaddr_in) };
345
346                if sockaddr.sin_family == AF_INET as i16 {
347                    gateway_ip = Some(net::Ipv4Addr::from(sockaddr.sin_addr.s_addr.to_ne_bytes()));
348                    break;
349                }
350
351                gateway = r_gateway.next;
352            }
353
354            let mut unicast_addr = cur_addr_r.first_unicast_address;
355            while !unicast_addr.is_null() {
356                let unicast_addr_r = unsafe { &mut *unicast_addr };
357
358                let sockaddr =
359                    unsafe { &*(unicast_addr_r.address.lp_sockaddr as *const ccs::sockaddr) };
360
361                match sockaddr.sa_family as usize {
362                    ccs::AF_INET => {
363                        if ipv4.is_some() {
364                            unicast_addr = unicast_addr_r.next;
365                            continue;
366                        }
367
368                        let sockaddr = unsafe {
369                            &*(unicast_addr_r.address.lp_sockaddr as *const ccs::sockaddr_in)
370                        };
371
372                        ipv4 = Some(net::Ipv4Addr::from(sockaddr.sin_addr.s_addr.to_ne_bytes()))
373                    }
374                    ccs::AF_INET6 => {
375                        if ipv6.is_some() {
376                            unicast_addr = unicast_addr_r.next;
377                            continue;
378                        }
379
380                        let sockaddr = unsafe {
381                            &*(unicast_addr_r.address.lp_sockaddr as *const ccs::sockaddr_in6)
382                        };
383
384                        ipv6 = Some(net::Ipv6Addr::from(unsafe { sockaddr.sin6_addr.s6_addr }))
385                    }
386                    _ => {}
387                }
388
389                unicast_addr = unicast_addr_r.next
390            }
391
392            output = Some((ipv4, ipv6, gateway_ip, mac, guid, name))
393        }
394
395        cur_addr = cur_addr_r.next
396    }
397
398    let output = match output {
399        Some(output) => output,
400        None => {
401            return Err(io::Error::new(
402                io::ErrorKind::NotFound,
403                format!("there isn\'t any adapter with id {}", if_id),
404            ))
405        }
406    };
407
408    Ok(output)
409}