Finish implementing io::net::addrinfo

This fills in the `hints` structure and exposes libuv's full functionality for
doing dns lookups.
This commit is contained in:
Alex Crichton 2013-10-15 20:37:39 -07:00
parent bac9681858
commit 1db783bdcf
7 changed files with 262 additions and 92 deletions

View File

@ -0,0 +1,127 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Synchronous DNS Resolution
Contains the functionality to perform DNS resolution in a style related to
getaddrinfo()
*/
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::io_error;
use rt::io::net::ip::{SocketAddr, IpAddr};
use rt::rtio::{IoFactory, IoFactoryObject};
use rt::local::Local;
/// Hints to the types of sockets that are desired when looking up hosts
pub enum SocketType {
Stream, Datagram, Raw
}
/// Flags which can be or'd into the `flags` field of a `Hint`. These are used
/// to manipulate how a query is performed.
///
/// The meaning of each of these flags can be found with `man -s 3 getaddrinfo`
pub enum Flag {
AddrConfig,
All,
CanonName,
NumericHost,
NumericServ,
Passive,
V4Mapped,
}
/// A transport protocol associated with either a hint or a return value of
/// `lookup`
pub enum Protocol {
TCP, UDP
}
/// This structure is used to provide hints when fetching addresses for a
/// remote host to control how the lookup is performed.
///
/// For details on these fields, see their corresponding definitions via
/// `man -s 3 getaddrinfo`
pub struct Hint {
family: uint,
socktype: Option<SocketType>,
protocol: Option<Protocol>,
flags: uint,
}
pub struct Info {
address: SocketAddr,
family: uint,
socktype: Option<SocketType>,
protocol: Option<Protocol>,
flags: uint,
}
/// Easy name resolution. Given a hostname, returns the list of IP addresses for
/// that hostname.
///
/// # Failure
///
/// On failure, this will raise on the `io_error` condition.
pub fn get_host_addresses(host: &str) -> Option<~[IpAddr]> {
lookup(Some(host), None, None).map(|a| a.map(|i| i.address.ip))
}
/// Full-fleged resolution. This function will perform a synchronous call to
/// getaddrinfo, controlled by the parameters
///
/// # Arguments
///
/// * hostname - an optional hostname to lookup against
/// * servname - an optional service name, listed in the system services
/// * hint - see the hint structure, and "man -s 3 getaddrinfo", for how this
/// controls lookup
///
/// # Failure
///
/// On failure, this will raise on the `io_error` condition.
pub fn lookup(hostname: Option<&str>, servname: Option<&str>,
hint: Option<Hint>) -> Option<~[Info]> {
let ipaddrs = unsafe {
let io: *mut IoFactoryObject = Local::unsafe_borrow();
(*io).get_host_addresses(hostname, servname, hint)
};
match ipaddrs {
Ok(i) => Some(i),
Err(ioerr) => {
io_error::cond.raise(ioerr);
None
}
}
}
#[cfg(test)]
mod test {
use option::Some;
use rt::io::net::ip::Ipv4Addr;
use super::*;
#[test]
fn dns_smoke_test() {
let ipaddrs = get_host_addresses("localhost").unwrap();
let mut found_local = false;
let local_addr = &Ipv4Addr(127, 0, 0, 1);
for addr in ipaddrs.iter() {
found_local = found_local || addr == local_addr;
}
assert!(found_local);
}
}

View File

@ -8,55 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::io_error;
use rt::io::net::ip::IpAddr;
use rt::rtio::{IoFactory, IoFactoryObject};
use rt::local::Local;
pub use self::addrinfo::get_host_addresses;
pub mod addrinfo;
pub mod tcp;
pub mod udp;
pub mod ip;
#[cfg(unix)]
pub mod unix;
/// Simplistic name resolution
pub fn get_host_addresses(host: &str) -> Option<~[IpAddr]> {
/*!
* Get the IP addresses for a given host name.
*
* Raises io_error on failure.
*/
let ipaddrs = unsafe {
let io: *mut IoFactoryObject = Local::unsafe_borrow();
(*io).get_host_addresses(host)
};
match ipaddrs {
Ok(i) => Some(i),
Err(ioerr) => {
io_error::cond.raise(ioerr);
None
}
}
}
#[cfg(test)]
mod test {
use option::Some;
use rt::io::net::ip::Ipv4Addr;
use super::*;
#[test]
fn dns_smoke_test() {
let ipaddrs = get_host_addresses("localhost").unwrap();
let mut found_local = false;
let local_addr = &Ipv4Addr(127, 0, 0, 1);
for addr in ipaddrs.iter() {
found_local = found_local || addr == local_addr;
}
assert!(found_local);
}
}

View File

@ -13,6 +13,7 @@ use option::*;
use result::*;
use libc::c_int;
use ai = rt::io::net::addrinfo;
use rt::io::IoError;
use super::io::process::ProcessConfig;
use super::io::net::ip::{IpAddr, SocketAddr};
@ -80,7 +81,8 @@ pub trait IoFactory {
fn fs_open<P: PathLike>(&mut self, path: &P, fm: FileMode, fa: FileAccess)
-> Result<~RtioFileStream, IoError>;
fn fs_unlink<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>;
fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>;
fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
hint: Option<ai::Hint>) -> Result<~[ai::Info], IoError>;
fn fs_stat<P: PathLike>(&mut self, path: &P) -> Result<FileStat, IoError>;
fn fs_mkdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>;
fn fs_rmdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>;

View File

@ -18,9 +18,10 @@ use rt::uv::uvll;
use rt::uv::uvll::UV_GETADDRINFO;
use rt::uv::{Loop, UvError, NativeHandle};
use rt::uv::status_to_maybe_uv_error;
use rt::uv::net::UvAddrInfo;
use rt::uv::net;
use ai = rt::io::net::addrinfo;
type GetAddrInfoCallback = ~fn(GetAddrInfoRequest, &UvAddrInfo, Option<UvError>);
type GetAddrInfoCallback = ~fn(GetAddrInfoRequest, &net::UvAddrInfo, Option<UvError>);
pub struct GetAddrInfoRequest(*uvll::uv_getaddrinfo_t);
@ -38,7 +39,7 @@ impl GetAddrInfoRequest {
}
pub fn getaddrinfo(&mut self, loop_: &Loop, node: Option<&str>,
service: Option<&str>, hints: Option<UvAddrInfo>,
service: Option<&str>, hints: Option<ai::Hint>,
cb: GetAddrInfoCallback) {
assert!(node.is_some() || service.is_some());
@ -72,8 +73,37 @@ impl GetAddrInfoRequest {
cb(req, addrinfo, err)
};
// XXX: Implement hints
assert!(hints.is_none());
let hint = hints.map(|hint| unsafe {
let mut flags = 0;
do each_ai_flag |cval, aival| {
if hint.flags & (aival as uint) != 0 {
flags |= cval as i32;
}
}
let socktype = match hint.socktype {
Some(ai::Stream) => uvll::rust_SOCK_STREAM(),
Some(ai::Datagram) => uvll::rust_SOCK_DGRAM(),
Some(ai::Raw) => uvll::rust_SOCK_RAW(),
None => 0,
};
let protocol = match hint.protocol {
Some(ai::UDP) => uvll::rust_IPPROTO_UDP(),
Some(ai::TCP) => uvll::rust_IPPROTO_TCP(),
_ => 0,
};
uvll::addrinfo {
ai_flags: flags,
ai_family: hint.family as c_int,
ai_socktype: socktype,
ai_protocol: protocol,
ai_addrlen: 0,
ai_canonname: null(),
ai_addr: null(),
ai_next: null(),
}
});
let hint_ptr = hint.as_ref().map_default(null(), |x| x as *uvll::addrinfo);
self.get_req_data().getaddrinfo_cb = Some(wrapper_cb);
@ -83,7 +113,7 @@ impl GetAddrInfoRequest {
getaddrinfo_cb,
c_node_ptr,
c_service_ptr,
null()));
hint_ptr));
}
extern "C" fn getaddrinfo_cb(req: *uvll::uv_getaddrinfo_t,
@ -91,7 +121,7 @@ impl GetAddrInfoRequest {
res: *uvll::addrinfo) {
let mut req: GetAddrInfoRequest = NativeHandle::from_native_handle(req);
let err = status_to_maybe_uv_error(status);
let addrinfo = UvAddrInfo(res);
let addrinfo = net::UvAddrInfo(res);
let data = req.get_req_data();
(*data.getaddrinfo_cb.get_ref())(req, &addrinfo, err);
unsafe {
@ -137,6 +167,66 @@ impl GetAddrInfoRequest {
}
}
fn each_ai_flag(f: &fn(c_int, ai::Flag)) {
unsafe {
f(uvll::rust_AI_ADDRCONFIG(), ai::AddrConfig);
f(uvll::rust_AI_ALL(), ai::All);
f(uvll::rust_AI_CANONNAME(), ai::CanonName);
f(uvll::rust_AI_NUMERICHOST(), ai::NumericHost);
f(uvll::rust_AI_NUMERICSERV(), ai::NumericServ);
f(uvll::rust_AI_PASSIVE(), ai::Passive);
f(uvll::rust_AI_V4MAPPED(), ai::V4Mapped);
}
}
// Traverse the addrinfo linked list, producing a vector of Rust socket addresses
pub fn accum_addrinfo(addr: &net::UvAddrInfo) -> ~[ai::Info] {
unsafe {
let &net::UvAddrInfo(addr) = addr;
let mut addr = addr;
let mut addrs = ~[];
loop {
let uvaddr = net::sockaddr_to_UvSocketAddr((*addr).ai_addr);
let rustaddr = net::uv_socket_addr_to_socket_addr(uvaddr);
let mut flags = 0;
do each_ai_flag |cval, aival| {
if (*addr).ai_flags & cval != 0 {
flags |= aival as uint;
}
}
let protocol = match (*addr).ai_protocol {
p if p == uvll::rust_IPPROTO_UDP() => Some(ai::UDP),
p if p == uvll::rust_IPPROTO_TCP() => Some(ai::TCP),
_ => None,
};
let socktype = match (*addr).ai_socktype {
p if p == uvll::rust_SOCK_STREAM() => Some(ai::Stream),
p if p == uvll::rust_SOCK_DGRAM() => Some(ai::Datagram),
p if p == uvll::rust_SOCK_RAW() => Some(ai::Raw),
_ => None,
};
addrs.push(ai::Info {
address: rustaddr,
family: (*addr).ai_family as uint,
socktype: socktype,
protocol: protocol,
flags: flags,
});
if (*addr).ai_next.is_not_null() {
addr = (*addr).ai_next;
} else {
break;
}
}
return addrs;
}
}
impl NativeHandle<*uvll::uv_getaddrinfo_t> for GetAddrInfoRequest {
fn from_native_handle(handle: *uvll::uv_getaddrinfo_t) -> GetAddrInfoRequest {
GetAddrInfoRequest(handle)
@ -150,7 +240,6 @@ impl NativeHandle<*uvll::uv_getaddrinfo_t> for GetAddrInfoRequest {
mod test {
use option::{Some, None};
use rt::uv::Loop;
use rt::uv::net::accum_sockaddrs;
use rt::io::net::ip::{SocketAddr, Ipv4Addr};
use super::*;
@ -159,14 +248,14 @@ mod test {
let mut loop_ = Loop::new();
let mut req = GetAddrInfoRequest::new();
do req.getaddrinfo(&loop_, Some("localhost"), None, None) |_, addrinfo, _| {
let sockaddrs = accum_sockaddrs(addrinfo);
let sockaddrs = accum_addrinfo(addrinfo);
let mut found_local = false;
let local_addr = &SocketAddr {
ip: Ipv4Addr(127, 0, 0, 1),
port: 0
};
for addr in sockaddrs.iter() {
found_local = found_local || addr == local_addr;
found_local = found_local || addr.address == *local_addr;
}
assert!(found_local);
}

View File

@ -27,7 +27,7 @@ pub enum UvSocketAddr {
UvIpv6SocketAddr(*sockaddr_in6),
}
fn sockaddr_to_UvSocketAddr(addr: *uvll::sockaddr) -> UvSocketAddr {
pub fn sockaddr_to_UvSocketAddr(addr: *uvll::sockaddr) -> UvSocketAddr {
unsafe {
assert!((is_ip4_addr(addr) || is_ip6_addr(addr)));
assert!(!(is_ip4_addr(addr) && is_ip6_addr(addr)));
@ -96,28 +96,6 @@ pub fn uv_socket_addr_to_socket_addr(addr: UvSocketAddr) -> SocketAddr {
uv_socket_addr_as_socket_addr(addr, util::id)
}
// Traverse the addrinfo linked list, producing a vector of Rust socket addresses
pub fn accum_sockaddrs(addr: &UvAddrInfo) -> ~[SocketAddr] {
unsafe {
let &UvAddrInfo(addr) = addr;
let mut addr = addr;
let mut addrs = ~[];
loop {
let uvaddr = sockaddr_to_UvSocketAddr((*addr).ai_addr);
let rustaddr = uv_socket_addr_to_socket_addr(uvaddr);
addrs.push(rustaddr);
if (*addr).ai_next.is_not_null() {
addr = (*addr).ai_next;
} else {
break;
}
}
return addrs;
}
}
#[cfg(test)]
#[test]
fn test_ip4_conversion() {
@ -232,13 +210,13 @@ impl StreamWatcher {
data.connect_cb = Some(cb);
}
unsafe {
return unsafe {
static BACKLOG: c_int = 128; // XXX should be configurable
match uvll::listen(self.native_handle(), BACKLOG, connection_cb) {
0 => Ok(()),
n => Err(UvError(n))
}
}
};
extern fn connection_cb(handle: *uvll::uv_stream_t, status: c_int) {
rtdebug!("connection_cb");
@ -466,12 +444,12 @@ impl NativeHandle<*uvll::uv_udp_t> for UdpWatcher {
}
// uv_connect_t is a subclass of uv_req_t
struct ConnectRequest(*uvll::uv_connect_t);
pub struct ConnectRequest(*uvll::uv_connect_t);
impl Request for ConnectRequest { }
impl ConnectRequest {
fn new() -> ConnectRequest {
pub fn new() -> ConnectRequest {
let connect_handle = unsafe { malloc_req(UV_CONNECT) };
assert!(connect_handle.is_not_null());
ConnectRequest(connect_handle as *uvll::uv_connect_t)

View File

@ -32,8 +32,8 @@ use rt::tube::Tube;
use rt::task::SchedHome;
use rt::uv::*;
use rt::uv::idle::IdleWatcher;
use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr, accum_sockaddrs};
use rt::uv::addrinfo::GetAddrInfoRequest;
use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr};
use rt::uv::addrinfo::{GetAddrInfoRequest, accum_addrinfo};
use unstable::sync::Exclusive;
use path::{GenericPath, Path};
use super::super::io::support::PathLike;
@ -43,6 +43,7 @@ use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create,
CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite,
FileStat};
use task;
use ai = rt::io::net::addrinfo;
#[cfg(test)] use container::Container;
#[cfg(test)] use unstable::run_in_bare_thread;
@ -658,12 +659,16 @@ impl IoFactory for UvIoFactory {
return result_cell.take();
}
fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError> {
fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
hint: Option<ai::Hint>) -> Result<~[ai::Info], IoError> {
let result_cell = Cell::new_empty();
let result_cell_ptr: *Cell<Result<~[IpAddr], IoError>> = &result_cell;
let host_ptr: *&str = &host;
let result_cell_ptr: *Cell<Result<~[ai::Info], IoError>> = &result_cell;
let host_ptr: *Option<&str> = &host;
let servname_ptr: *Option<&str> = &servname;
let hint_ptr: *Option<ai::Hint> = &hint;
let addrinfo_req = GetAddrInfoRequest::new();
let addrinfo_req_cell = Cell::new(addrinfo_req);
do task::unkillable { // FIXME(#8674)
let scheduler: ~Scheduler = Local::take();
do scheduler.deschedule_running_task_and_then |_, task| {
@ -671,10 +676,10 @@ impl IoFactory for UvIoFactory {
let mut addrinfo_req = addrinfo_req_cell.take();
unsafe {
do addrinfo_req.getaddrinfo(self.uv_loop(),
Some(*host_ptr),
None, None) |_, addrinfo, err| {
*host_ptr, *servname_ptr,
*hint_ptr) |_, addrinfo, err| {
let res = match err {
None => Ok(accum_sockaddrs(addrinfo).map(|addr| addr.ip.clone())),
None => Ok(accum_addrinfo(addrinfo)),
Some(err) => Err(uv_error_to_io_error(err))
};
(*result_cell_ptr).put_back(res);

View File

@ -637,3 +637,16 @@ extern "C" int
rust_uv_pipe_init(uv_loop_t *loop, uv_pipe_t* p, int ipc) {
return uv_pipe_init(loop, p, ipc);
}
extern "C" int rust_SOCK_STREAM() { return SOCK_STREAM; }
extern "C" int rust_SOCK_DGRAM() { return SOCK_DGRAM; }
extern "C" int rust_SOCK_RAW() { return SOCK_RAW; }
extern "C" int rust_IPPROTO_UDP() { return IPPROTO_UDP; }
extern "C" int rust_IPPROTO_TCP() { return IPPROTO_TCP; }
extern "C" int rust_AI_ADDRCONFIG() { return AI_ADDRCONFIG; }
extern "C" int rust_AI_ALL() { return AI_ALL; }
extern "C" int rust_AI_CANONNAME() { return AI_CANONNAME; }
extern "C" int rust_AI_NUMERICHOST() { return AI_NUMERICHOST; }
extern "C" int rust_AI_NUMERICSERV() { return AI_NUMERICSERV; }
extern "C" int rust_AI_PASSIVE() { return AI_PASSIVE; }
extern "C" int rust_AI_V4MAPPED() { return AI_V4MAPPED; }