#[doc=" 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 create_uv_getaddrinfo_t = uv::ll::getaddrinfo_t; import set_data_for_uv_handle = uv::ll::set_data_for_uv_handle; import get_data_for_uv_handle = uv::ll::get_data_for_uv_handle; import ll = uv::ll; export ip_addr, parse_addr_err; export format_addr; export v4, v6; export get_addr; #[doc = "An IP address"] enum ip_addr { #[doc="An IPv4 address"] ipv4(sockaddr_in), ipv6(sockaddr_in6) } #[doc=" Human-friendly feedback on why a parse_addr attempt failed "] type parse_addr_err = { err_msg: str }; #[doc=" Convert a `ip_addr` to a str # Arguments * ip - a `std::net::ip::ip_addr` "] fn format_addr(ip: ip_addr) -> str { alt ip { ipv4(addr) { unsafe { let result = uv_ip4_name(&addr); if result == "" { fail "failed to convert inner sockaddr_in address to str" } result } } ipv6(addr) { unsafe { let result = uv_ip6_name(&addr); if result == "" { fail "failed to convert inner sockaddr_in address to str" } result } } } } type get_addr_data = { output_ch: comm::chan> }; crust fn get_addr_cb(handle: *uv_getaddrinfo_t, status: libc::c_int, res: *addrinfo) unsafe { let handle_data = get_data_for_uv_handle(handle) as *get_addr_data; if status == 0i32 { if res != (ptr::null::()) { let mut out_vec = []; let mut curr_addr = res; loop { if ll::is_ipv4_addrinfo(curr_addr) { out_vec += [ipv4(copy(( *ll::addrinfo_as_sockaddr_in(curr_addr))))]; } else { out_vec += [ipv6(copy(( *ll::addrinfo_as_sockaddr_in6(curr_addr))))]; } let next_addr = ll::get_next_addrinfo(curr_addr); if next_addr == ptr::null::() as *addrinfo { break; } else { curr_addr = next_addr } } (*handle_data).output_ch.send(result::ok(out_vec)); } else { (*handle_data).output_ch.send( result::err(get_addr_unknown_error)); } } else { (*handle_data).output_ch.send( result::err(get_addr_unknown_error)); } } #[doc=" "] enum ip_get_addr_err { get_addr_unknown_error } #[doc=" "] fn get_addr(++node: str, iotask: iotask) -> result::result<[ip_addr], ip_get_addr_err> unsafe { comm::listen {|output_ch| str::unpack_slice(node) {|node_ptr, len| log(debug, #fmt("sliace 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); 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_uv_handle(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 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 { alt try_parse_addr(ip) { // FIXME: more copies brought to light to due the implicit // copy compiler warning.. what can be done? out pointers, // ala c#? result::ok(addr) { copy(addr) } result::err(err_data) { fail err_data.err_msg } } } fn try_parse_addr(ip: str) -> result::result { unsafe { // need to figure out how to establish a parse failure.. 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)); // here we're going to let inaddr_none_val = "255.255.255.255"; if ip != inaddr_none_val && reformatted_name == inaddr_none_val { result::err({err_msg:#fmt("failed to parse '%s'", ip)}) } else { result::ok(ipv4(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) { // FIXME: more copies brought to light to due the implicit // copy compiler warning.. what can be done? out pointers, // ala c#? result::ok(addr) { copy(addr) } result::err(err_data) { fail err_data.err_msg } } } fn try_parse_addr(ip: str) -> result::result { 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)) } } } } //#[cfg(test)] mod test { #[test] fn test_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_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_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] fn test_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); } } } }