rust/src/libstd/net_ip.rs

387 lines
12 KiB
Rust
Raw Normal View History

//! Types/fns concerning Internet Protocol (IP), versions 4 & 6
import vec;
import uint;
2012-08-29 19:41:38 -05:00
import iotask = uv::iotask::IoTask;
import interact = uv::iotask::interact;
import sockaddr_in = uv::ll::sockaddr_in;
import sockaddr_in6 = uv::ll::sockaddr_in6;
import addrinfo = uv::ll::addrinfo;
import uv_getaddrinfo_t = uv::ll::uv_getaddrinfo_t;
import uv_ip4_addr = uv::ll::ip4_addr;
import uv_ip4_name = uv::ll::ip4_name;
import uv_ip6_addr = uv::ll::ip6_addr;
import uv_ip6_name = uv::ll::ip6_name;
import uv_getaddrinfo = uv::ll::getaddrinfo;
import uv_freeaddrinfo = uv::ll::freeaddrinfo;
import create_uv_getaddrinfo_t = uv::ll::getaddrinfo_t;
import set_data_for_req = uv::ll::set_data_for_req;
import get_data_for_req = uv::ll::get_data_for_req;
import ll = uv::ll;
2012-08-14 16:17:27 -05:00
import comm = core::comm;
export ip_addr, parse_addr_err;
2012-04-30 17:10:24 -05:00
export format_addr;
export v4, v6;
export get_addr;
export ipv4, ipv6;
/// An IP address
enum ip_addr {
/// An IPv4 address
ipv4(sockaddr_in),
ipv6(sockaddr_in6)
}
/// Human-friendly feedback on why a parse_addr attempt failed
type parse_addr_err = {
err_msg: ~str
};
/**
* Convert a `ip_addr` to a str
*
* # Arguments
*
* * ip - a `std::net::ip::ip_addr`
*/
fn format_addr(ip: ip_addr) -> ~str {
2012-08-06 14:34:08 -05:00
match ip {
2012-08-03 21:59:04 -05:00
ipv4(addr) => unsafe {
let result = uv_ip4_name(&addr);
if result == ~"" {
fail ~"failed to convert inner sockaddr_in address to str"
}
2012-08-03 21:59:04 -05:00
result
},
2012-08-03 21:59:04 -05:00
ipv6(addr) => unsafe {
let result = uv_ip6_name(&addr);
if result == ~"" {
fail ~"failed to convert inner sockaddr_in address to str"
}
2012-08-03 21:59:04 -05:00
result
}
}
}
/// Represents errors returned from `net::ip::get_addr()`
enum ip_get_addr_err {
get_addr_unknown_error
}
/**
* Attempts name resolution on the provided `node` string
*
* # Arguments
*
* * `node` - a string representing some host address
* * `iotask` - a `uv::iotask` used to interact with the underlying event loop
*
* # Returns
*
* A `result<~[ip_addr], ip_get_addr_err>` instance that will contain
* a vector of `ip_addr` results, in the case of success, or an error
* object in the case of failure
*/
fn get_addr(++node: ~str, iotask: iotask)
2012-08-26 18:54:31 -05:00
-> result::Result<~[ip_addr], ip_get_addr_err> {
2012-08-14 16:17:27 -05:00
do core::comm::listen |output_ch| {
do str::as_buf(node) |node_ptr, len| unsafe {
2012-08-22 19:24:52 -05:00
log(debug, fmt!("slice len %?", len));
let handle = create_uv_getaddrinfo_t();
let handle_ptr = ptr::addr_of(handle);
let handle_data: get_addr_data = {
output_ch: output_ch
};
let handle_data_ptr = ptr::addr_of(handle_data);
do interact(iotask) |loop_ptr| unsafe {
let result = uv_getaddrinfo(
loop_ptr,
handle_ptr,
get_addr_cb,
node_ptr,
ptr::null(),
ptr::null());
2012-08-06 14:34:08 -05:00
match result {
2012-08-03 21:59:04 -05:00
0i32 => {
set_data_for_req(handle_ptr, handle_data_ptr);
}
2012-08-03 21:59:04 -05:00
_ => {
2012-08-26 18:54:31 -05:00
output_ch.send(result::Err(get_addr_unknown_error));
}
}
};
output_ch.recv()
}
}
}
mod v4 {
/**
* Convert a str to `ip_addr`
*
* # Failure
*
* Fails if the string is not a valid IPv4 address
*
* # Arguments
*
* * ip - a string of the format `x.x.x.x`
*
* # Returns
*
* * an `ip_addr` of the `ipv4` variant
*/
fn parse_addr(ip: ~str) -> ip_addr {
2012-08-06 14:34:08 -05:00
match try_parse_addr(ip) {
2012-08-26 18:54:31 -05:00
result::Ok(addr) => copy(addr),
result::Err(err_data) => fail err_data.err_msg
}
}
// the simple, old style numberic representation of
// ipv4
type ipv4_rep = { a: u8, b: u8, c: u8, d:u8 };
trait as_unsafe_u32 {
unsafe fn as_u32() -> u32;
}
2012-08-07 20:10:06 -05:00
impl ipv4_rep: as_unsafe_u32 {
// this is pretty dastardly, i know
unsafe fn as_u32() -> u32 {
*((ptr::addr_of(self)) as *u32)
}
}
2012-08-26 18:54:31 -05:00
fn parse_to_ipv4_rep(ip: ~str) -> result::Result<ipv4_rep, ~str> {
2012-06-30 18:19:07 -05:00
let parts = vec::map(str::split_char(ip, '.'), |s| {
2012-08-06 14:34:08 -05:00
match uint::from_str(s) {
2012-08-20 14:23:37 -05:00
Some(n) if n <= 255u => n,
2012-08-03 21:59:04 -05:00
_ => 256u
}
});
if vec::len(parts) != 4u {
2012-08-26 18:54:31 -05:00
result::Err(fmt!("'%s' doesn't have 4 parts", ip))
}
else if vec::contains(parts, 256u) {
2012-08-26 18:54:31 -05:00
result::Err(fmt!("invalid octal in addr '%s'", ip))
}
else {
2012-08-26 18:54:31 -05:00
result::Ok({a: parts[0] as u8, b: parts[1] as u8,
c: parts[2] as u8, d: parts[3] as u8})
}
}
2012-08-26 18:54:31 -05:00
fn try_parse_addr(ip: ~str) -> result::Result<ip_addr,parse_addr_err> {
unsafe {
let INADDR_NONE = ll::get_INADDR_NONE();
let ip_rep_result = parse_to_ipv4_rep(ip);
if result::is_err(ip_rep_result) {
let err_str = result::get_err(ip_rep_result);
2012-08-26 18:54:31 -05:00
return result::Err({err_msg: err_str})
}
// ipv4_rep.as_u32 is unsafe :/
let input_is_inaddr_none =
result::get(ip_rep_result).as_u32() == INADDR_NONE;
let new_addr = uv_ip4_addr(ip, 22);
let reformatted_name = uv_ip4_name(&new_addr);
2012-08-22 19:24:52 -05:00
log(debug, fmt!("try_parse_addr: input ip: %s reparsed ip: %s",
ip, reformatted_name));
let ref_ip_rep_result = parse_to_ipv4_rep(reformatted_name);
if result::is_err(ref_ip_rep_result) {
let err_str = result::get_err(ref_ip_rep_result);
2012-08-26 18:54:31 -05:00
return result::Err({err_msg: err_str})
}
if result::get(ref_ip_rep_result).as_u32() == INADDR_NONE &&
!input_is_inaddr_none {
2012-08-26 18:54:31 -05:00
return result::Err(
{err_msg: ~"uv_ip4_name produced invalid result."})
}
else {
2012-08-26 18:54:31 -05:00
result::Ok(ipv4(copy(new_addr)))
}
}
}
}
mod v6 {
/**
* Convert a str to `ip_addr`
*
* # Failure
*
* Fails if the string is not a valid IPv6 address
*
* # Arguments
*
* * ip - an ipv6 string. See RFC2460 for spec.
*
* # Returns
*
* * an `ip_addr` of the `ipv6` variant
*/
fn parse_addr(ip: ~str) -> ip_addr {
2012-08-06 14:34:08 -05:00
match try_parse_addr(ip) {
2012-08-26 18:54:31 -05:00
result::Ok(addr) => copy(addr),
result::Err(err_data) => fail err_data.err_msg
}
}
2012-08-26 18:54:31 -05:00
fn try_parse_addr(ip: ~str) -> result::Result<ip_addr,parse_addr_err> {
unsafe {
// need to figure out how to establish a parse failure..
let new_addr = uv_ip6_addr(ip, 22);
let reparsed_name = uv_ip6_name(&new_addr);
2012-08-22 19:24:52 -05:00
log(debug, fmt!("v6::try_parse_addr ip: '%s' reparsed '%s'",
ip, reparsed_name));
// '::' appears to be uv_ip6_name() returns for bogus
// parses..
if ip != ~"::" && reparsed_name == ~"::" {
2012-08-26 18:54:31 -05:00
result::Err({err_msg:fmt!("failed to parse '%s'",
2012-08-22 19:24:52 -05:00
ip)})
}
else {
2012-08-26 18:54:31 -05:00
result::Ok(ipv6(new_addr))
}
}
}
}
type get_addr_data = {
2012-08-26 18:54:31 -05:00
output_ch: comm::Chan<result::Result<~[ip_addr],ip_get_addr_err>>
};
2012-07-03 18:32:02 -05:00
extern fn get_addr_cb(handle: *uv_getaddrinfo_t, status: libc::c_int,
res: *addrinfo) unsafe {
log(debug, ~"in get_addr_cb");
let handle_data = get_data_for_req(handle) as
*get_addr_data;
if status == 0i32 {
if res != (ptr::null::<addrinfo>()) {
let mut out_vec = ~[];
2012-08-22 19:24:52 -05:00
log(debug, fmt!("initial addrinfo: %?", res));
let mut curr_addr = res;
loop {
let new_ip_addr = if ll::is_ipv4_addrinfo(curr_addr) {
ipv4(copy((
*ll::addrinfo_as_sockaddr_in(curr_addr))))
}
else if ll::is_ipv6_addrinfo(curr_addr) {
ipv6(copy((
*ll::addrinfo_as_sockaddr_in6(curr_addr))))
}
else {
log(debug, ~"curr_addr is not of family AF_INET or "+
~"AF_INET6. Error.");
(*handle_data).output_ch.send(
2012-08-26 18:54:31 -05:00
result::Err(get_addr_unknown_error));
break;
};
out_vec += ~[new_ip_addr];
let next_addr = ll::get_next_addrinfo(curr_addr);
if next_addr == ptr::null::<addrinfo>() as *addrinfo {
log(debug, ~"null next_addr encountered. no mas");
break;
}
else {
curr_addr = next_addr;
2012-08-22 19:24:52 -05:00
log(debug, fmt!("next_addr addrinfo: %?", curr_addr));
}
}
2012-08-22 19:24:52 -05:00
log(debug, fmt!("successful process addrinfo result, len: %?",
vec::len(out_vec)));
2012-08-26 18:54:31 -05:00
(*handle_data).output_ch.send(result::Ok(out_vec));
}
else {
log(debug, ~"addrinfo pointer is NULL");
(*handle_data).output_ch.send(
2012-08-26 18:54:31 -05:00
result::Err(get_addr_unknown_error));
}
}
else {
log(debug, ~"status != 0 error in get_addr_cb");
(*handle_data).output_ch.send(
2012-08-26 18:54:31 -05:00
result::Err(get_addr_unknown_error));
}
if res != (ptr::null::<addrinfo>()) {
uv_freeaddrinfo(res);
}
log(debug, ~"leaving get_addr_cb");
}
#[cfg(test)]
mod test {
#[test]
2012-06-26 18:13:50 -05:00
fn test_ip_ipv4_parse_and_format_ip() {
let localhost_str = ~"127.0.0.1";
assert (format_addr(v4::parse_addr(localhost_str))
== localhost_str)
}
#[test]
2012-06-26 18:13:50 -05:00
fn test_ip_ipv6_parse_and_format_ip() {
let localhost_str = ~"::1";
let format_result = format_addr(v6::parse_addr(localhost_str));
2012-08-22 19:24:52 -05:00
log(debug, fmt!("results: expected: '%s' actual: '%s'",
localhost_str, format_result));
assert format_result == localhost_str;
}
#[test]
2012-06-26 18:13:50 -05:00
fn test_ip_ipv4_bad_parse() {
2012-08-06 14:34:08 -05:00
match v4::try_parse_addr(~"b4df00d") {
2012-08-26 18:54:31 -05:00
result::Err(err_info) => {
2012-08-22 19:24:52 -05:00
log(debug, fmt!("got error as expected %?", err_info));
assert true;
}
2012-08-26 18:54:31 -05:00
result::Ok(addr) => {
2012-08-22 19:24:52 -05:00
fail fmt!("Expected failure, but got addr %?", addr);
}
}
}
#[test]
#[ignore(target_os="win32")]
2012-06-26 18:13:50 -05:00
fn test_ip_ipv6_bad_parse() {
2012-08-06 14:34:08 -05:00
match v6::try_parse_addr(~"::,~2234k;") {
2012-08-26 18:54:31 -05:00
result::Err(err_info) => {
2012-08-22 19:24:52 -05:00
log(debug, fmt!("got error as expected %?", err_info));
assert true;
}
2012-08-26 18:54:31 -05:00
result::Ok(addr) => {
2012-08-22 19:24:52 -05:00
fail fmt!("Expected failure, but got addr %?", addr);
}
}
}
#[test]
2012-06-29 17:35:47 -05:00
#[ignore(reason = "valgrind says it's leaky")]
2012-06-26 18:13:50 -05:00
fn test_ip_get_addr() {
let localhost_name = ~"localhost";
let iotask = uv::global_loop::get();
let ga_result = get_addr(localhost_name, iotask);
if result::is_err(ga_result) {
fail ~"got err result from net::ip::get_addr();"
}
// note really sure how to realiably test/assert
// this.. mostly just wanting to see it work, atm.
let results = result::unwrap(ga_result);
2012-08-22 19:24:52 -05:00
log(debug, fmt!("test_get_addr: Number of results for %s: %?",
localhost_name, vec::len(results)));
for vec::each(results) |r| {
2012-08-06 14:34:08 -05:00
let ipv_prefix = match r {
2012-08-03 21:59:04 -05:00
ipv4(_) => ~"IPv4",
ipv6(_) => ~"IPv6"
};
2012-08-22 19:24:52 -05:00
log(debug, fmt!("test_get_addr: result %s: '%s'",
ipv_prefix, format_addr(r)));
}
// at least one result.. this is going to vary from system
// to system, based on stuff like the contents of /etc/hosts
assert vec::len(results) > 0;
}
#[test]
2012-06-29 17:35:47 -05:00
#[ignore(reason = "valgrind says it's leaky")]
2012-06-26 18:13:50 -05:00
fn test_ip_get_addr_bad_input() {
let localhost_name = ~"sjkl234m,./sdf";
let iotask = uv::global_loop::get();
let ga_result = get_addr(localhost_name, iotask);
assert result::is_err(ga_result);
}
}