1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/// The host name and IP address database.
///
/// This database provides queries for host names and IP addresses associated
/// with network hosts. It allows lookups based on a given host name or a
/// given IP address.

use std::{io, mem};
use std::net::IpAddr;
use std::str::FromStr;
use domain::bits::DNameBuf;
use futures::{Async, Future, Poll};
use tokio_core::reactor;


//============ Low-level API =================================================
//
// Currently private.

mod dns;
mod files;


//============ High-level API ================================================

/// Returns host information for a given host name.
///
/// The name is either a hostname, an IPv4 or IPv6 address in its standard
/// text notation. In the latter two cases, no lookups are performed and a
/// `HostEnt` is returned with `name` as the canonical name, the parsed
/// address as the sole address, and no aliases.
///
/// Otherwise the name is interpreted as a host name and lookups according to
/// the system configuraition are performed.
///
/// The function waits for all necessary IO to resolve. Upon success, it
/// returns a `HostEnt` value if a host for the given name was found or
/// `Ok(None)` otherwise.
///
/// # Limitations
///
/// For this initial version of the crate, the lookup is a `files` lookup
/// first and only if that does fail to yield a result, a DNS query for
/// both A and AAAA records. This initial version also does not yet fill
/// the aliases list of the returned `HostEnt`.
pub fn get_host_by_name(name: &str) -> Result<Option<HostEnt>, io::Error> {
    let mut core = reactor::Core::new()?;
    let handle = core.handle();
    core.run(poll_host_by_name(name, &handle))
}

/// Returns host information for a given IP address.
///
/// The IP address can either be an IPv4 or IPv6 address. The function waits
/// for all necessary IO to resolve. Upon success, it
/// returns a `HostEnt` value if a host for the given name was found or
/// `Ok(None)` otherwise.
///
/// # Limitations
///
/// For this initial version of the crate, the lookup is a `files` lookup
/// first and only if that does fail to yield a result, a DNS query for
/// PTR records. This initial version also does not yet fill
/// the aliases list of the returned `HostEnt`.
pub fn get_host_by_addr(addr: IpAddr) -> Result<Option<HostEnt>, io::Error> {
    let mut core = reactor::Core::new()?;
    let handle = core.handle();
    core.run(poll_host_by_addr(addr, &handle))
}

/// Returns host information for a given host name.
///
/// The name is either a hostname, an IPv4 or IPv6 address in its standard
/// text notation. In the latter two cases, no lookups are performed and a
/// `HostEnt` is returned with `name` as the canonical name, the parsed
/// address as the sole address, and no aliases.
///
/// Otherwise the name is interpreted as a host name and lookups according to
/// the system configuraition are performed.
///
/// The function returns a future that performes all necessary IO via the
/// Tokio reactor given by `reactor`.
///
/// # Limitations
///
/// For this initial version of the crate, the lookup is a `files` lookup
/// first and only if that does fail to yield a result, a DNS query for
/// both A and AAAA records. This initial version also does not yet fill
/// the aliases list of the returned `HostEnt`.
pub fn poll_host_by_name(name: &str, reactor: &reactor::Handle)
                         -> HostByName {
    HostByName::new(name, reactor)
}

/// Returns host information for a given IP address.
///
/// The IP address can either be an IPv4 or IPv6 address. The function returns
/// a future performing all necessary IO via the Tokio reactor given by
/// `reactor`.
///
/// # Limitations
///
/// For this initial version of the crate, the lookup is a `files` lookup
/// first and only if that does fail to yield a result, a DNS query for
/// PTR records. This initial version also does not yet fill
/// the aliases list of the returned `HostEnt`.
pub fn poll_host_by_addr(addr: IpAddr, reactor: &reactor::Handle)
                         -> HostByAddr {
    HostByAddr::new(addr, reactor)
}


//------------ HostEnt -------------------------------------------------------

/// The result of a host lookup.
///
/// > **Note.** This implementation is highly temporary. While will probably
/// > keep the semantics, the actual types may change. 
pub struct HostEnt {
    name: String,
    aliases: Vec<String>,
    addrs: Vec<IpAddr>,
}

impl HostEnt {
    /// The canoncial name of the host.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// The aliases of the host.
    ///
    /// > **Note.** Best to assume this is a slice of `str`.
    pub fn aliases(&self) -> &[String] {
        self.aliases.as_ref()
    }

    /// The addresses of the host.
    pub fn addrs(&self) -> &[IpAddr] {
        self.addrs.as_ref()
    }
}


//------------ HostByName ----------------------------------------------------

/// The future returned by `poll_host_by_name()`.
///
/// Resolves into a `HostEnt` value if the lookup is successful or `None` if
/// there is no such name.
pub struct HostByName(ByNameInner);

enum ByNameInner {
    Files(HostEnt),
    Dns(dns::HostByName),
    Error(io::Error),
    Done,
}

impl HostByName {
    pub fn new(name: &str, reactor: &reactor::Handle) -> Self {
        if let Ok(addr) = IpAddr::from_str(name) {
            return HostByName(ByNameInner::Files(HostEnt {
                name: name.into(),
                aliases: Vec::new(),
                addrs: vec!(addr),
            }))
        }
        let name = match DNameBuf::from_str(name) {
            Ok(name) => name,
            Err(e) => {
                return HostByName(ByNameInner::Error(
                    io::Error::new(io::ErrorKind::Other, e)
                ))
            }
        };
        HostByName(match files::get_host_by_name(&name) {
            Ok(Some(ent)) => ByNameInner::Files(ent),
            Ok(None) => ByNameInner::Dns(dns::HostByName::new(name, reactor)),
            Err(err) => ByNameInner::Error(err),
        })
    }
}


impl Future for HostByName {
    type Item = Option<HostEnt>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if let ByNameInner::Dns(ref mut lookup) = self.0 {
            return lookup.poll();
        }
        match mem::replace(&mut self.0, ByNameInner::Done) {
            ByNameInner::Files(res) => Ok(Async::Ready(Some(res))),
            ByNameInner::Error(err) => Err(err),
            ByNameInner::Done => panic!("polling a resolved HostByName"),
            _ => panic!()
        }
    }
}


//------------ HostByAddr ----------------------------------------------------

/// The future returned by `poll_host_by_addr()`.
///
/// Resolves into a `HostEnt` value if the lookup is successful or `None` if
/// there is no such address.
pub struct HostByAddr(ByAddrInner);

enum ByAddrInner {
    Files(HostEnt),
    Dns(dns::HostByAddr),
    Error(io::Error),
    Done
}

impl HostByAddr {
    pub fn new(addr: IpAddr, reactor: &reactor::Handle) -> Self {
        HostByAddr(match files::get_host_by_addr(addr) {
            Ok(Some(ent)) => ByAddrInner::Files(ent),
            Ok(None) => ByAddrInner::Dns(dns::HostByAddr::new(addr, reactor)),
            Err(err) => ByAddrInner::Error(err),
        })
    }
}

impl Future for HostByAddr {
    type Item = Option<HostEnt>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        if let ByAddrInner::Dns(ref mut lookup) = self.0 {
            return lookup.poll();
        }
        match mem::replace(&mut self.0, ByAddrInner::Done) {
            ByAddrInner::Files(res) => Ok(Async::Ready(Some(res))),
            ByAddrInner::Error(err) => Err(err),
            ByAddrInner::Done => panic!("polling a resolved HostByAddr"),
            _ => panic!()
        }
    }
}