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}