Merge remote-tracking branch 'brson/uv'
Conflicts: src/libstd/net_ip.rs src/libstd/net_tcp.rs
This commit is contained in:
commit
6d411342c6
7 changed files with 1303 additions and 585 deletions
|
@ -4,16 +4,35 @@ Types/fns concerning Internet Protocol (IP), versions 4 & 6
|
|||
|
||||
import vec;
|
||||
import uint;
|
||||
import iotask = uv::iotask::iotask;
|
||||
import interact = uv::iotask::interact;
|
||||
import comm::methods;
|
||||
|
||||
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;
|
||||
|
||||
export ip_addr, parse_addr_err;
|
||||
export format_addr;
|
||||
export v4;
|
||||
export v4, v6;
|
||||
export get_addr;
|
||||
|
||||
#[doc = "An IP address"]
|
||||
enum ip_addr {
|
||||
#[doc="An IPv4 address"]
|
||||
ipv4(u8, u8, u8, u8),
|
||||
ipv6(u16,u16,u16,u16,u16,u16,u16,u16)
|
||||
ipv4(sockaddr_in),
|
||||
ipv6(sockaddr_in6)
|
||||
}
|
||||
|
||||
#[doc="
|
||||
|
@ -32,22 +51,88 @@ Convert a `ip_addr` to a str
|
|||
"]
|
||||
fn format_addr(ip: ip_addr) -> str {
|
||||
alt ip {
|
||||
ipv4(a, b, c, d) {
|
||||
#fmt["%u.%u.%u.%u", a as uint, b as uint, c as uint, d as uint]
|
||||
ipv4(addr) {
|
||||
unsafe {
|
||||
let result = uv_ip4_name(&addr);
|
||||
if result == "" {
|
||||
fail "failed to convert inner sockaddr_in address to str"
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
ipv6(_, _, _, _, _, _, _, _) {
|
||||
fail "FIXME (#2651) impl parsing of ipv6 addr";
|
||||
ipv6(addr) {
|
||||
unsafe {
|
||||
let result = uv_ip6_name(&addr);
|
||||
if result == "" {
|
||||
fail "failed to convert inner sockaddr_in address to str"
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc="
|
||||
Represents errors returned from `net::ip::get_addr()`
|
||||
"]
|
||||
enum ip_get_addr_err {
|
||||
get_addr_unknown_error
|
||||
}
|
||||
|
||||
#[doc="
|
||||
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)
|
||||
-> result::result<[ip_addr]/~, ip_get_addr_err> unsafe {
|
||||
do comm::listen |output_ch| {
|
||||
do str::unpack_slice(node) |node_ptr, len| {
|
||||
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| {
|
||||
let result = uv_getaddrinfo(
|
||||
loop_ptr,
|
||||
handle_ptr,
|
||||
get_addr_cb,
|
||||
node_ptr,
|
||||
ptr::null(),
|
||||
ptr::null());
|
||||
alt result {
|
||||
0i32 {
|
||||
set_data_for_req(handle_ptr, handle_data_ptr);
|
||||
}
|
||||
_ {
|
||||
output_ch.send(result::err(get_addr_unknown_error));
|
||||
}
|
||||
}
|
||||
};
|
||||
output_ch.recv()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod v4 {
|
||||
#[doc = "
|
||||
Convert a str to `ip_addr`
|
||||
|
||||
# Failure
|
||||
|
||||
j Fails if the string is not a valid IPv4 address
|
||||
Fails if the string is not a valid IPv4 address
|
||||
|
||||
# Arguments
|
||||
|
||||
|
@ -59,13 +144,22 @@ j Fails if the string is not a valid IPv4 address
|
|||
"]
|
||||
fn parse_addr(ip: str) -> ip_addr {
|
||||
alt try_parse_addr(ip) {
|
||||
result::ok(addr) { addr }
|
||||
result::ok(addr) { copy(addr) }
|
||||
result::err(err_data) {
|
||||
fail err_data.err_msg
|
||||
}
|
||||
}
|
||||
}
|
||||
fn try_parse_addr(ip: str) -> result::result<ip_addr,parse_addr_err> {
|
||||
// the simple, old style numberic representation of
|
||||
// ipv4
|
||||
type ipv4_rep = { a: u8, b: u8, c: u8, d:u8 };
|
||||
impl x for ipv4_rep {
|
||||
// this is pretty dastardly, i know
|
||||
unsafe fn as_u32() -> u32 {
|
||||
*((ptr::addr_of(self)) as *u32)
|
||||
}
|
||||
}
|
||||
fn parse_to_ipv4_rep(ip: str) -> result::result<ipv4_rep, str> {
|
||||
let parts = vec::map(str::split_char(ip, '.'), |s| {
|
||||
alt uint::from_str(s) {
|
||||
some(n) if n <= 255u { n }
|
||||
|
@ -73,31 +167,232 @@ j Fails if the string is not a valid IPv4 address
|
|||
}
|
||||
});
|
||||
if vec::len(parts) != 4u {
|
||||
result::err({err_msg: #fmt("'%s' doesn't have 4 parts",
|
||||
ip)})
|
||||
}
|
||||
result::err(#fmt("'%s' doesn't have 4 parts", ip))
|
||||
}
|
||||
else if vec::contains(parts, 256u) {
|
||||
result::err({err_msg: #fmt("invalid octal in provided addr '%s'",
|
||||
ip)})
|
||||
}
|
||||
result::err(#fmt("invalid octal in addr '%s'", ip))
|
||||
}
|
||||
else {
|
||||
result::ok(ipv4(parts[0] as u8, parts[1] as u8,
|
||||
parts[2] as u8, parts[3] as u8))
|
||||
result::ok({a: parts[0] as u8, b: parts[1] as u8,
|
||||
c: parts[2] as u8, d: parts[3] as u8})
|
||||
}
|
||||
}
|
||||
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);
|
||||
ret 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);
|
||||
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);
|
||||
ret result::err({err_msg: err_str})
|
||||
}
|
||||
if result::get(ref_ip_rep_result).as_u32() == INADDR_NONE &&
|
||||
!input_is_inaddr_none {
|
||||
ret result::err(
|
||||
{err_msg: "uv_ip4_name produced invalid result."})
|
||||
}
|
||||
else {
|
||||
result::ok(ipv4(copy(new_addr)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mod v6 {
|
||||
#[doc = "
|
||||
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 {
|
||||
alt try_parse_addr(ip) {
|
||||
result::ok(addr) { copy(addr) }
|
||||
result::err(err_data) {
|
||||
fail err_data.err_msg
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
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 == "::" {
|
||||
result::err({err_msg:#fmt("failed to parse '%s'",
|
||||
ip)})
|
||||
}
|
||||
else {
|
||||
result::ok(ipv6(new_addr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type get_addr_data = {
|
||||
output_ch: comm::chan<result::result<[ip_addr]/~,ip_get_addr_err>>
|
||||
};
|
||||
|
||||
crust 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 = []/~;
|
||||
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(
|
||||
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;
|
||||
log(debug, #fmt("next_addr addrinfo: %?", curr_addr));
|
||||
}
|
||||
}
|
||||
log(debug, #fmt("successful process addrinfo result, len: %?",
|
||||
vec::len(out_vec)));
|
||||
(*handle_data).output_ch.send(result::ok(out_vec));
|
||||
}
|
||||
else {
|
||||
log(debug, "addrinfo pointer is NULL");
|
||||
(*handle_data).output_ch.send(
|
||||
result::err(get_addr_unknown_error));
|
||||
}
|
||||
}
|
||||
else {
|
||||
log(debug, "status != 0 error in get_addr_cb");
|
||||
(*handle_data).output_ch.send(
|
||||
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]
|
||||
fn test_format_ip() {
|
||||
assert (format_addr(ipv4(127u8, 0u8, 0u8, 1u8))
|
||||
== "127.0.0.1")
|
||||
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]
|
||||
fn test_parse_ip() {
|
||||
assert (v4::parse_addr("127.0.0.1") ==
|
||||
ipv4(127u8, 0u8, 0u8, 1u8));
|
||||
fn test_ip_ipv6_parse_and_format_ip() {
|
||||
let localhost_str = "::1";
|
||||
let format_result = format_addr(v6::parse_addr(localhost_str));
|
||||
log(debug, #fmt("results: expected: '%s' actual: '%s'",
|
||||
localhost_str, format_result));
|
||||
assert format_result == localhost_str;
|
||||
}
|
||||
#[test]
|
||||
fn test_ip_ipv4_bad_parse() {
|
||||
alt v4::try_parse_addr("b4df00d") {
|
||||
result::err(err_info) {
|
||||
log(debug, #fmt("got error as expected %?", err_info));
|
||||
assert true;
|
||||
}
|
||||
result::ok(addr) {
|
||||
fail #fmt("Expected failure, but got addr %?", addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[ignore(target_os="win32")]
|
||||
fn test_ip_ipv6_bad_parse() {
|
||||
alt v6::try_parse_addr("::,~2234k;") {
|
||||
result::err(err_info) {
|
||||
log(debug, #fmt("got error as expected %?", err_info));
|
||||
assert true;
|
||||
}
|
||||
result::ok(addr) {
|
||||
fail #fmt("Expected failure, but got addr %?", addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[ignore(reason = "valgrind says it's leaky")]
|
||||
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);
|
||||
log(debug, #fmt("test_get_addr: Number of results for %s: %?",
|
||||
localhost_name, vec::len(results)));
|
||||
for vec::each(results) |r| {
|
||||
let ipv_prefix = alt r {
|
||||
ipv4(_) {
|
||||
"IPv4"
|
||||
}
|
||||
ipv6(_) {
|
||||
"IPv6"
|
||||
}
|
||||
};
|
||||
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]
|
||||
#[ignore(reason = "valgrind says it's leaky")]
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue