cdns_rs/portable.rs
1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 *
4 * Copyright (C) 2020 Aleksandr Morozov
5 *
6 * Copyright 2025 Aleksandr Morozov
7 *
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 *
11 * You may not use this work except in compliance with the Licence.
12 *
13 * You may obtain a copy of the Licence at:
14 *
15 * https://joinup.ec.europa.eu/software/page/eupl
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24use std::
25{
26 ffi::{CStr, CString},
27 mem::MaybeUninit,
28 net::{IpAddr, Ipv4Addr, Ipv6Addr}
29};
30
31use nix::libc;
32
33use crate::{error::*, internal_error, internal_error_map};
34
35/// This file contains code which should be manually ported to other
36/// OSes because it is OS specific or there is no support in rust's libc.
37
38
39
40/// A network interface info
41pub struct IfInfo
42{
43 /// A pointer to the start of the linked list with IF data
44 if_inf: *mut libc::ifaddrs,
45}
46
47impl Drop for IfInfo
48{
49 fn drop(&mut self)
50 {
51 if self.if_inf.is_null() == true
52 {
53 return;
54 }
55
56 unsafe
57 {
58 libc::freeifaddrs(self.if_inf);
59 }
60 }
61}
62
63impl IfInfo
64{
65 //const NI_MAXHOST: usize = 1025;
66
67 /// Creates an instance with pointer to the linked list with interfaces in system.
68 pub unsafe
69 fn get_interfaces_info() -> CDnsResult<Self>
70 {
71 let mut addrs = MaybeUninit::<*mut libc::ifaddrs>::uninit();
72
73 let ifaddrs = libc::getifaddrs(addrs.as_mut_ptr());
74
75 if ifaddrs == -1
76 {
77 internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
78 }
79
80 return Ok(
81 Self { if_inf: addrs.assume_init() }
82 );
83 }
84
85 /// Returns the interface IP address if inteface exists.
86 pub unsafe
87 fn get_ifr_ip(&self, ifr: &str, ip_addr: &IpAddr) -> CDnsResult<Option<IpAddr>>
88 {
89 if self.if_inf.is_null() == true
90 {
91 panic!("if_inf is NULL");
92 }
93
94 let c_ifr =
95 CString::new(ifr)
96 .map_err(|e|
97 internal_error_map!(CDnsErrorType::InternalError, "{}", e)
98 )?;
99
100 let mut tmp = self.if_inf;
101 while tmp.is_null() == false
102 {
103 if (*tmp).ifa_name.is_null() == true
104 {
105 tmp = (*tmp).ifa_next;
106 continue;
107 }
108
109 // convert raw char ptr to cstr
110 let name = CStr::from_ptr((*tmp).ifa_name);
111
112 // match title
113 if name != c_ifr.as_c_str()
114 {
115 tmp = (*tmp).ifa_next;
116 continue;
117 }
118
119 let addr = (*tmp).ifa_addr;
120
121 if addr.is_null() == false
122 {
123
124 let mut host = [0_u8; libc::NI_MAXHOST as usize];
125
126 let fam = (*addr).sa_family;
127
128 if fam as i32 == libc::AF_INET && ip_addr.is_ipv4() == true
129 {
130 let s =
131 libc::getnameinfo(
132 addr,
133 std::mem::size_of::<libc::sockaddr_in>() as u32,
134 host.as_mut_ptr() as *mut i8,
135 libc::NI_MAXHOST,
136 std::ptr::null_mut(),
137 0,
138 libc::NI_NUMERICHOST
139 );
140
141 if s != 0
142 {
143 internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
144 }
145
146 let c =
147 CStr::from_ptr(host.as_ptr() as *const i8)
148 .to_str()
149 .map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?;
150
151 let ip4: Ipv4Addr = c.parse().map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?;
152
153 return Ok(Some(IpAddr::from(ip4)));
154 }
155 else if fam as i32 == libc::AF_INET6 && ip_addr.is_ipv6() == true
156 {
157 let s =
158 libc::getnameinfo(
159 addr,
160 std::mem::size_of::<libc::sockaddr_in6>() as u32,
161 host.as_mut_ptr() as *mut i8,
162 libc::NI_MAXHOST,
163 std::ptr::null_mut(),
164 0,
165 libc::NI_NUMERICHOST
166 );
167
168 if s != 0
169 {
170 internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
171 }
172
173 let c =
174 CStr::from_ptr(host.as_ptr() as *const i8)
175 .to_str()
176 .map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?;
177
178 let ip6: Ipv6Addr =
179 c
180 .split_once("%")
181 .map_or(c, |(ip, _)| ip)
182 .parse()
183 .map_err(|e|
184 internal_error_map!(CDnsErrorType::InternalError, "{}", e)
185 )?;
186
187 return Ok(Some(IpAddr::from(Ipv6Addr::from(ip6))));
188 }
189 }
190
191 // next
192 tmp = (*tmp).ifa_next;
193 }
194
195 return Ok(None);
196 }
197
198}