cdns-rs 2.0.0-next.0

A native Sync/Async Rust implementation of client DNS resolver.
Documentation
/*-
 * cdns-rs - a simple sync/async DNS query library
 * 
 * Copyright (C) 2021  Aleksandr Morozov
 * Copyright (C) 2025  Aleksandr Morozov
 * Copyright (C) 2026  Aleksandr Morozov, 4neko.org
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

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

use nix::{ifaddrs::InterfaceAddressIterator, 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
#[derive(Debug)]
pub struct IfInfo;

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

    /// Creates an instance with pointer to the linked list with interfaces in system.
    pub 
    fn get_interfaces_info() -> CDnsResult<Self>
    {
        return Ok(Self);
    }

    /// Returns the interface IP address if inteface exists.
    pub 
    fn get_ifr_ip(&self, ifr: &str, ip_addr: &IpAddr) -> CDnsResult<Option<IpAddr>>
    {
        let if_ing =  
            nix::ifaddrs::getifaddrs()
                .map_err(|e|
                    internal_error_map!(CDnsErrorType::InternalError, "getifaddrs(): error: {}", e)
                )?;

        for ifi in if_ing
        {
            if ifi.interface_name.as_str() == ifr
            {
                let Some(if_addr) = ifi.address
                    else { continue; };

                if let Some(ip4) = if_addr.as_sockaddr_in() && ip_addr.is_ipv4() == true 
                {
                    return Ok(Some(IpAddr::V4(ip4.ip())));
                }
                else if let Some(ip6) = if_addr.as_sockaddr_in6() && ip_addr.is_ipv6() == true
                {
                    return Ok(Some(IpAddr::V6(ip6.ip())));
                }
            }
        }

        return Ok(None);
    }

}

//todo: https://docs.rs/nix/0.31.3/nix/ifaddrs/fn.getifaddrs.html
/*
/// A network interface info
pub struct IfInfo
{
    /// A pointer to the start of the linked list with IF data
    if_inf: NonNull<libc::ifaddrs>,
}

impl Drop for IfInfo
{
    fn drop(&mut self) 
    {
        unsafe
        {
            libc::freeifaddrs(self.if_inf.as_ptr());
        }
    }
}

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 { NonNull::new_unchecked(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>>
    {
        let c_ifr = 
            CString::new(ifr)
                .map_err(|e| 
                    internal_error_map!(CDnsErrorType::InternalError, "{}", e)
                )?;

        let mut tmp = self.if_inf.as_ptr();
        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);
    }

}
*/