cdns-rs 1.0.0

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
/*-
 * cdns-rs - a simple sync/async DNS query library
 * 
 * Copyright (C) 2020  Aleksandr Morozov
 * 
 * Copyright 2025 Aleksandr Morozov
 * 
 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence").
 * 
 * You may not use this work except in compliance with the Licence.
 * 
 * You may obtain a copy of the Licence at:
 * 
 *    https://joinup.ec.europa.eu/software/page/eupl
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * Licence for the specific language governing permissions and limitations
 * under the Licence.
 */

use std::
{
    ffi::{CStr, CString}, 
    mem::MaybeUninit, 
    net::{IpAddr, Ipv4Addr, Ipv6Addr}
};

use nix::libc;

use crate::{error::*, internal_error, internal_error_map};

/// This file contains code which should be manually ported to other
/// OSes because it is OS specific or there is no support in rust's libc.



/// A network interface info
pub struct IfInfo
{
    /// A pointer to the start of the linked list with IF data
    if_inf: *mut libc::ifaddrs,
}

impl Drop for IfInfo
{
    fn drop(&mut self) 
    {
        if self.if_inf.is_null() == true
        {
            return;
        }

        unsafe
        {
            libc::freeifaddrs(self.if_inf);
        }
    }
}

impl IfInfo
{
    //const NI_MAXHOST: usize = 1025;

    /// Creates an instance with pointer to the linked list with interfaces in system.
    pub unsafe
    fn get_interfaces_info() -> CDnsResult<Self>
    {
        let mut addrs = MaybeUninit::<*mut libc::ifaddrs>::uninit();

        let ifaddrs = unsafe { libc::getifaddrs(addrs.as_mut_ptr()) };

        if ifaddrs == -1
        {
            internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
        }

        return Ok(
            Self { if_inf: unsafe { addrs.assume_init() } }
        );
    }

    /// Returns the interface IP address if inteface exists.
    pub unsafe
    fn get_ifr_ip(&self, ifr: &str, ip_addr: &IpAddr) -> CDnsResult<Option<IpAddr>>
    {
        if self.if_inf.is_null() == true
        {
            panic!("if_inf is NULL");
        }

        let c_ifr = 
            CString::new(ifr)
                .map_err(|e| 
                    internal_error_map!(CDnsErrorType::InternalError, "{}", e)
                )?;

        let mut tmp = self.if_inf;
        while tmp.is_null() == false
        {
            unsafe 
            {
                if (*tmp).ifa_name.is_null() == true
                {
                    tmp = (*tmp).ifa_next;
                    continue;
                }
            }

            // convert raw char ptr to cstr
            let name = unsafe { CStr::from_ptr((*tmp).ifa_name) };

            // match title
            if name != c_ifr.as_c_str()
            {
                unsafe { tmp = (*tmp).ifa_next };
                continue;
            }

            let addr = unsafe { (*tmp).ifa_addr };

            if addr.is_null() == false
            {
                
                let mut host = [0_u8; libc::NI_MAXHOST as usize];

                let fam = unsafe { (*addr).sa_family };

                if fam as i32 == libc::AF_INET && ip_addr.is_ipv4() == true
                {
                    let s = 
                        unsafe {
                            libc::getnameinfo(
                                addr,
                                std::mem::size_of::<libc::sockaddr_in>() as u32,
                                host.as_mut_ptr() as *mut i8, 
                                libc::NI_MAXHOST,
                                std::ptr::null_mut(), 
                                0, 
                                libc::NI_NUMERICHOST
                            )
                        };

                    if s != 0
                    {
                        internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
                    }

                    let c = 
                        unsafe {
                            CStr::from_ptr(host.as_ptr() as *const i8)
                                .to_str()
                                .map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
                        };
                    
                    let ip4: Ipv4Addr = c.parse().map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?;
                    
                    return Ok(Some(IpAddr::from(ip4)));
                }
                else if fam as i32 == libc::AF_INET6 && ip_addr.is_ipv6() == true
                {
                    let s = 
                        unsafe 
                        {
                            libc::getnameinfo(
                                addr,
                                std::mem::size_of::<libc::sockaddr_in6>() as u32,
                                host.as_mut_ptr() as *mut i8, 
                                libc::NI_MAXHOST,
                                std::ptr::null_mut(), 
                                0, 
                                libc::NI_NUMERICHOST
                            )
                        };

                    if s != 0
                    {
                        internal_error!(CDnsErrorType::InternalError, "{}", std::io::Error::last_os_error());
                    }

                    let c = 
                        unsafe 
                        {
                            CStr::from_ptr(host.as_ptr() as *const i8)
                                .to_str()
                                .map_err(|e| internal_error_map!(CDnsErrorType::InternalError, "{}", e))?
                        };
                    
                    let ip6: Ipv6Addr = 
                        c
                            .split_once("%")
                            .map_or(c, |(ip, _)| ip)
                            .parse()
                            .map_err(|e| 
                                internal_error_map!(CDnsErrorType::InternalError, "{}", e)
                            )?;
                   
                    return Ok(Some(IpAddr::from(Ipv6Addr::from(ip6))));
                }
            }

            // next
            tmp = unsafe { (*tmp).ifa_next };
        }

        return Ok(None);
    }

}