resolv 0.4.0

DNS resolution via glibc
Documentation
//! This library consists of a high-level interface to Gnu libc's (glibc) `libresolv` DNS
//! resolver.  It allows you to look up DNS resource records of any type (e.g. A, AAAA, MX, TXT,
//! etc), use recursion (if your system's DNS resolver permits it), and perform the DNS search
//! algorithm to complete incomplete names, all via your operating system (glibc, not the kernel).
//!
//! The lower level library, libresolv-sys, was generated from glibc version 2.23 on linux,
//! using the newer thread-safe function calls.  It may not work on older version of glibc, or
//! on other operating systems.  Pull-requests which improve portability are appreciated.
//!
//! # Example
//!
//! ````
//! extern crate resolv;
//!
//! use resolv::{Resolver, Class, RecordType, Section, Record};
//! use resolv::record::MX;
//!
//! fn main() {
//!     // You must create a mutable resolver object to hold the context.
//!     let mut resolver = Resolver::new().unwrap();
//!
//!     // .query() and .search() are the main interfaces to the resolver.
//!     let mut response = resolver.query(b"gmail.com", Class::IN,
//!                                       RecordType::MX).unwrap();
//!
//!     // .get_section_count() returns the number of records in that
//!     // section of the response.  There are four sections, but we
//!     // usually care about `Section::Answer`
//!     for i in 0..response.get_section_count(Section::Answer) {
//!
//!         // As records are strongly typed, you must know what you are
//!         // looking for.  If you assign into the wrong type, a run-time
//!         // error `WrongRRType` will be returned.
//!         let mx: Record<MX> = response.get_record(Section::Answer, i)
//!                              .unwrap();
//!
//!        println!("{:?}", mx);
//!     }
//! }
//! ````

extern crate byteorder;
extern crate libresolv_sys;

pub mod error;
use error::{Error, ResolutionError};

mod response;
pub use response::{Flags, RecordItems, Response, Section};

pub mod record;
pub use record::{Class, Record, RecordType};

#[cfg(test)]
mod tests;

use std::ffi::CString;
use std::ops::{Deref, DerefMut};

type Context = libresolv_sys::__res_state;

pub use libresolv_sys::ResolverOption;

pub struct Resolver {
    context: Context,
}

impl Resolver {
    pub fn new() -> Option<Resolver> {
        let mut resolver = Resolver {
            context: libresolv_sys::__res_state::default(),
        };

        if unsafe { libresolv_sys::res_ninit(&mut resolver.context) } != 0 {
            return None;
        }

        resolver.option(ResolverOption::Default, true);

        Some(resolver)
    }

    /// Set or unset an option
    pub fn option(&mut self, option: ResolverOption, value: bool) {
        if value {
            self.context.options = self.context.options | (option as u64);
        } else {
            self.context.options = self.context.options & !(option as u64);
        }
    }

    /// Lookup the record.  Applies the search algorithm to the domain name given
    /// (if not fully qualified, it completes it using rules specified in `resolv.conf`
    /// search entries).  In addition, this also searches your hosts file.  Applies
    /// recursion if available and not turned off (it is on by default).
    ///
    /// This is the highest level resolver routine, and is the one called by
    /// gethostbyname.
    pub fn search(
        &mut self,
        name: &[u8],
        class: Class,
        typ: RecordType,
    ) -> Result<Response, Error> {
        let name = match CString::new(name) {
            Ok(c) => c,
            Err(n) => return Err(Error::CString(n)),
        };
        let buflen: usize = libresolv_sys::NS_PACKETSZ as usize;
        let mut buffer: Box<Vec<u8>> = Box::new(Vec::with_capacity(buflen));

        let rlen: i32 = unsafe {
            libresolv_sys::res_nsearch(
                &mut self.context,
                name.as_ptr(),
                class as i32,
                typ as i32,
                buffer.deref_mut().as_mut_ptr(),
                buflen as i32,
            )
        };
        if rlen == -1 {
            return Err(From::from(self.get_error()));
        }

        let mut msg: libresolv_sys::ns_msg = libresolv_sys::ns_msg::default();
        unsafe {
            if libresolv_sys::ns_initparse(buffer.deref().as_ptr(), rlen, &mut msg) < 0 {
                return Err(Error::ParseError);
            }
        }

        Ok(Response::new(msg, buffer))
    }

    /// Lookup the record.  Does not apply the search algorithm, so `dname` must be a complete
    /// domain name, and only DNS will be consulted (not your hosts file).  Applies recursion
    /// if available and not turned off (it is on by default).
    pub fn query(
        &mut self,
        dname: &[u8],
        class: Class,
        typ: RecordType,
    ) -> Result<Response, Error> {
        let name = match CString::new(dname) {
            Ok(c) => c,
            Err(n) => return Err(Error::CString(n)),
        };
        let buflen: usize = libresolv_sys::NS_PACKETSZ as usize;
        let mut buffer: Box<Vec<u8>> = Box::new(Vec::with_capacity(buflen));

        let rlen: i32 = unsafe {
            libresolv_sys::res_nquery(
                &mut self.context,
                name.as_ptr(),
                class as i32,
                typ as i32,
                buffer.deref_mut().as_mut_ptr(),
                buflen as i32,
            )
        };
        if rlen == -1 {
            return Err(From::from(self.get_error()));
        }

        let mut msg: libresolv_sys::ns_msg = libresolv_sys::ns_msg::default();
        unsafe {
            if libresolv_sys::ns_initparse(buffer.deref().as_ptr(), rlen, &mut msg) < 0 {
                return Err(Error::ParseError);
            }
        }

        Ok(Response::new(msg, buffer))
    }

    fn get_error(&self) -> ResolutionError {
        match self.context.res_h_errno {
            0 => ResolutionError::Success,
            1 => ResolutionError::HostNotFound,
            2 => ResolutionError::TryAgain,
            3 => ResolutionError::NoRecovery,
            4 => ResolutionError::NoData,
            _ => ResolutionError::HostNotFound, // fallback
        }
    }
}