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 #[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 #[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}