net_ip.rs 12.1 KB
Newer Older
1
//! Types/fns concerning Internet Protocol (IP), versions 4 & 6
2

P
Patrick Walton 已提交
3 4
use iotask = uv::iotask::IoTask;
use interact = uv::iotask::interact;
5

P
Patrick Walton 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
use sockaddr_in = uv::ll::sockaddr_in;
use sockaddr_in6 = uv::ll::sockaddr_in6;
use addrinfo = uv::ll::addrinfo;
use uv_getaddrinfo_t = uv::ll::uv_getaddrinfo_t;
use uv_ip4_addr = uv::ll::ip4_addr;
use uv_ip4_name = uv::ll::ip4_name;
use uv_ip6_addr = uv::ll::ip6_addr;
use uv_ip6_name = uv::ll::ip6_name;
use uv_getaddrinfo = uv::ll::getaddrinfo;
use uv_freeaddrinfo = uv::ll::freeaddrinfo;
use create_uv_getaddrinfo_t = uv::ll::getaddrinfo_t;
use set_data_for_req = uv::ll::set_data_for_req;
use get_data_for_req = uv::ll::get_data_for_req;
use ll = uv::ll;
use comm = core::comm;
21

B
Brian Anderson 已提交
22
export IpAddr, parse_addr_err;
J
Jeff Olson 已提交
23
export format_addr;
24 25
export v4, v6;
export get_addr;
B
Brian Anderson 已提交
26
export Ipv4, Ipv6;
27

28
/// An IP address
B
Brian Anderson 已提交
29
enum IpAddr {
30
    /// An IPv4 address
B
Brian Anderson 已提交
31 32
    Ipv4(sockaddr_in),
    Ipv6(sockaddr_in6)
33 34
}

35
/// Human-friendly feedback on why a parse_addr attempt failed
B
Brian Anderson 已提交
36
type ParseAddrErr = {
37
    err_msg: ~str
38 39
};

40 41 42 43 44 45 46
/**
 * Convert a `ip_addr` to a str
 *
 * # Arguments
 *
 * * ip - a `std::net::ip::ip_addr`
 */
B
Brian Anderson 已提交
47
fn format_addr(ip: IpAddr) -> ~str {
48
    match ip {
B
Brian Anderson 已提交
49
      Ipv4(addr) =>  unsafe {
B
Brian Anderson 已提交
50 51 52
        let result = uv_ip4_name(&addr);
        if result == ~"" {
            fail ~"failed to convert inner sockaddr_in address to str"
53
        }
B
Brian Anderson 已提交
54
        result
55
      },
B
Brian Anderson 已提交
56
      Ipv6(addr) => unsafe {
B
Brian Anderson 已提交
57 58 59
        let result = uv_ip6_name(&addr);
        if result == ~"" {
            fail ~"failed to convert inner sockaddr_in address to str"
60
        }
B
Brian Anderson 已提交
61
        result
62
      }
63 64 65
    }
}

66
/// Represents errors returned from `net::ip::get_addr()`
B
Brian Anderson 已提交
67 68
enum IpGetAddrErr {
    GetAddrUnknownError
69 70
}

71 72 73 74 75 76 77 78 79 80
/**
 * 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
 *
81
 * A `result<~[ip_addr], ip_get_addr_err>` instance that will contain
82 83 84
 * a vector of `ip_addr` results, in the case of success, or an error
 * object in the case of failure
 */
85
fn get_addr(++node: ~str, iotask: iotask)
B
Brian Anderson 已提交
86
        -> result::Result<~[IpAddr], IpGetAddrErr> {
E
Eric Holk 已提交
87
    do core::comm::listen |output_ch| {
88
        do str::as_buf(node) |node_ptr, len| unsafe {
P
Paul Stansifer 已提交
89
            log(debug, fmt!("slice len %?", len));
90 91
            let handle = create_uv_getaddrinfo_t();
            let handle_ptr = ptr::addr_of(handle);
B
Brian Anderson 已提交
92
            let handle_data: GetAddrData = {
93 94 95
                output_ch: output_ch
            };
            let handle_data_ptr = ptr::addr_of(handle_data);
96
            do interact(iotask) |loop_ptr| unsafe {
97 98 99 100 101 102 103
                let result = uv_getaddrinfo(
                    loop_ptr,
                    handle_ptr,
                    get_addr_cb,
                    node_ptr,
                    ptr::null(),
                    ptr::null());
104
                match result {
B
Brian Anderson 已提交
105
                  0i32 => {
106
                    set_data_for_req(handle_ptr, handle_data_ptr);
107
                  }
B
Brian Anderson 已提交
108
                  _ => {
B
Brian Anderson 已提交
109
                    output_ch.send(result::Err(GetAddrUnknownError));
110 111 112 113 114 115 116 117
                  }
                }
            };
            output_ch.recv()
        }
    }
}

118
mod v4 {
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    /**
     * 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
     */
B
Brian Anderson 已提交
134
    fn parse_addr(ip: ~str) -> IpAddr {
135
        match try_parse_addr(ip) {
136 137
          result::Ok(addr) => copy(addr),
          result::Err(err_data) => fail err_data.err_msg
138 139
        }
    }
J
Jeff Olson 已提交
140 141
    // the simple, old style numberic representation of
    // ipv4
B
Brian Anderson 已提交
142
    type Ipv4Rep = { a: u8, b: u8, c: u8, d:u8 };
143

B
Brian Anderson 已提交
144
    trait AsUnsafeU32 {
145 146 147
        unsafe fn as_u32() -> u32;
    }

B
Brian Anderson 已提交
148
    impl Ipv4Rep: AsUnsafeU32 {
J
Jeff Olson 已提交
149 150 151 152 153
        // this is pretty dastardly, i know
        unsafe fn as_u32() -> u32 {
            *((ptr::addr_of(self)) as *u32)
        }
    }
B
Brian Anderson 已提交
154
    fn parse_to_ipv4_rep(ip: ~str) -> result::Result<Ipv4Rep, ~str> {
B
Brian Anderson 已提交
155
        let parts = vec::map(str::split_char(ip, '.'), |s| {
156
            match uint::from_str(s) {
B
Brian Anderson 已提交
157
              Some(n) if n <= 255u => n,
B
Brian Anderson 已提交
158
              _ => 256u
159 160
            }
        });
161
        if vec::len(parts) != 4u {
162
                result::Err(fmt!("'%s' doesn't have 4 parts", ip))
J
Jeff Olson 已提交
163
                }
164
        else if vec::contains(parts, 256u) {
165
                result::Err(fmt!("invalid octal in addr '%s'", ip))
J
Jeff Olson 已提交
166 167
                }
        else {
168
            result::Ok({a: parts[0] as u8, b: parts[1] as u8,
J
Jeff Olson 已提交
169 170 171
                        c: parts[2] as u8, d: parts[3] as u8})
        }
    }
B
Brian Anderson 已提交
172
    fn try_parse_addr(ip: ~str) -> result::Result<IpAddr,ParseAddrErr> {
173
        unsafe {
J
Jeff Olson 已提交
174
            let INADDR_NONE = ll::get_INADDR_NONE();
175
            let ip_rep_result = parse_to_ipv4_rep(ip);
J
Jeff Olson 已提交
176 177
            if result::is_err(ip_rep_result) {
                let err_str = result::get_err(ip_rep_result);
178
                return result::Err({err_msg: err_str})
J
Jeff Olson 已提交
179 180 181 182 183
            }
            // ipv4_rep.as_u32 is unsafe :/
            let input_is_inaddr_none =
                result::get(ip_rep_result).as_u32() == INADDR_NONE;

184 185
            let new_addr = uv_ip4_addr(ip, 22);
            let reformatted_name = uv_ip4_name(&new_addr);
P
Paul Stansifer 已提交
186 187
            log(debug, fmt!("try_parse_addr: input ip: %s reparsed ip: %s",
                            ip, reformatted_name));
188
            let ref_ip_rep_result = parse_to_ipv4_rep(reformatted_name);
J
Jeff Olson 已提交
189 190
            if result::is_err(ref_ip_rep_result) {
                let err_str = result::get_err(ref_ip_rep_result);
191
                return result::Err({err_msg: err_str})
J
Jeff Olson 已提交
192 193 194
            }
            if result::get(ref_ip_rep_result).as_u32() == INADDR_NONE &&
                 !input_is_inaddr_none {
195
                return result::Err(
196
                    {err_msg: ~"uv_ip4_name produced invalid result."})
197 198
            }
            else {
B
Brian Anderson 已提交
199
                result::Ok(Ipv4(copy(new_addr)))
200
            }
201
        }
202 203
    }
}
204
mod v6 {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    /**
     * 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
     */
B
Brian Anderson 已提交
220
    fn parse_addr(ip: ~str) -> IpAddr {
221
        match try_parse_addr(ip) {
222 223
          result::Ok(addr) => copy(addr),
          result::Err(err_data) => fail err_data.err_msg
224 225
        }
    }
B
Brian Anderson 已提交
226
    fn try_parse_addr(ip: ~str) -> result::Result<IpAddr,ParseAddrErr> {
227 228 229 230
        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);
P
Paul Stansifer 已提交
231 232
            log(debug, fmt!("v6::try_parse_addr ip: '%s' reparsed '%s'",
                            ip, reparsed_name));
233 234
            // '::' appears to be uv_ip6_name() returns for bogus
            // parses..
235
            if  ip != ~"::" && reparsed_name == ~"::" {
236
                result::Err({err_msg:fmt!("failed to parse '%s'",
P
Paul Stansifer 已提交
237
                                           ip)})
238 239
            }
            else {
B
Brian Anderson 已提交
240
                result::Ok(Ipv6(new_addr))
241 242 243 244 245
            }
        }
    }
}

B
Brian Anderson 已提交
246 247
type GetAddrData = {
    output_ch: comm::Chan<result::Result<~[IpAddr],IpGetAddrErr>>
248 249
};

G
Graydon Hoare 已提交
250
extern fn get_addr_cb(handle: *uv_getaddrinfo_t, status: libc::c_int,
251
                     res: *addrinfo) unsafe {
252
    log(debug, ~"in get_addr_cb");
253
    let handle_data = get_data_for_req(handle) as
B
Brian Anderson 已提交
254
        *GetAddrData;
255 256
    if status == 0i32 {
        if res != (ptr::null::<addrinfo>()) {
257
            let mut out_vec = ~[];
P
Paul Stansifer 已提交
258
            log(debug, fmt!("initial addrinfo: %?", res));
259 260 261
            let mut curr_addr = res;
            loop {
                let new_ip_addr = if ll::is_ipv4_addrinfo(curr_addr) {
B
Brian Anderson 已提交
262
                    Ipv4(copy((
263 264 265
                        *ll::addrinfo_as_sockaddr_in(curr_addr))))
                }
                else if ll::is_ipv6_addrinfo(curr_addr) {
B
Brian Anderson 已提交
266
                    Ipv6(copy((
267 268 269
                        *ll::addrinfo_as_sockaddr_in6(curr_addr))))
                }
                else {
270 271
                    log(debug, ~"curr_addr is not of family AF_INET or "+
                        ~"AF_INET6. Error.");
272
                    (*handle_data).output_ch.send(
B
Brian Anderson 已提交
273
                        result::Err(GetAddrUnknownError));
274 275
                    break;
                };
276
                out_vec += ~[new_ip_addr];
277 278 279

                let next_addr = ll::get_next_addrinfo(curr_addr);
                if next_addr == ptr::null::<addrinfo>() as *addrinfo {
280
                    log(debug, ~"null next_addr encountered. no mas");
281 282 283 284
                    break;
                }
                else {
                    curr_addr = next_addr;
P
Paul Stansifer 已提交
285
                    log(debug, fmt!("next_addr addrinfo: %?", curr_addr));
286 287
                }
            }
P
Paul Stansifer 已提交
288 289
            log(debug, fmt!("successful process addrinfo result, len: %?",
                            vec::len(out_vec)));
290
            (*handle_data).output_ch.send(result::Ok(out_vec));
291 292
        }
        else {
293
            log(debug, ~"addrinfo pointer is NULL");
294
            (*handle_data).output_ch.send(
B
Brian Anderson 已提交
295
                result::Err(GetAddrUnknownError));
296
        }
297
    }
298
    else {
299
        log(debug, ~"status != 0 error in get_addr_cb");
300
        (*handle_data).output_ch.send(
B
Brian Anderson 已提交
301
            result::Err(GetAddrUnknownError));
302 303 304 305
    }
    if res != (ptr::null::<addrinfo>()) {
        uv_freeaddrinfo(res);
    }
306
    log(debug, ~"leaving get_addr_cb");
307 308 309 310 311
}

#[cfg(test)]
mod test {
    #[test]
J
Jeff Olson 已提交
312
    fn test_ip_ipv4_parse_and_format_ip() {
313
        let localhost_str = ~"127.0.0.1";
314 315 316 317
        assert (format_addr(v4::parse_addr(localhost_str))
                == localhost_str)
    }
    #[test]
J
Jeff Olson 已提交
318
    fn test_ip_ipv6_parse_and_format_ip() {
319
        let localhost_str = ~"::1";
320
        let format_result = format_addr(v6::parse_addr(localhost_str));
P
Paul Stansifer 已提交
321 322
        log(debug, fmt!("results: expected: '%s' actual: '%s'",
            localhost_str, format_result));
323 324 325
        assert format_result == localhost_str;
    }
    #[test]
J
Jeff Olson 已提交
326
    fn test_ip_ipv4_bad_parse() {
327
        match v4::try_parse_addr(~"b4df00d") {
328
          result::Err(err_info) => {
P
Paul Stansifer 已提交
329
            log(debug, fmt!("got error as expected %?", err_info));
330 331
            assert true;
          }
332
          result::Ok(addr) => {
P
Paul Stansifer 已提交
333
            fail fmt!("Expected failure, but got addr %?", addr);
334 335 336 337
          }
        }
    }
    #[test]
338
    #[ignore(target_os="win32")]
J
Jeff Olson 已提交
339
    fn test_ip_ipv6_bad_parse() {
340
        match v6::try_parse_addr(~"::,~2234k;") {
341
          result::Err(err_info) => {
P
Paul Stansifer 已提交
342
            log(debug, fmt!("got error as expected %?", err_info));
343 344
            assert true;
          }
345
          result::Ok(addr) => {
P
Paul Stansifer 已提交
346
            fail fmt!("Expected failure, but got addr %?", addr);
347 348
          }
        }
349
    }
350
    #[test]
351
    #[ignore(reason = "valgrind says it's leaky")]
J
Jeff Olson 已提交
352
    fn test_ip_get_addr() {
353
        let localhost_name = ~"localhost";
354 355 356
        let iotask = uv::global_loop::get();
        let ga_result = get_addr(localhost_name, iotask);
        if result::is_err(ga_result) {
357
            fail ~"got err result from net::ip::get_addr();"
358 359 360 361
        }
        // note really sure how to realiably test/assert
        // this.. mostly just wanting to see it work, atm.
        let results = result::unwrap(ga_result);
P
Paul Stansifer 已提交
362 363
        log(debug, fmt!("test_get_addr: Number of results for %s: %?",
                        localhost_name, vec::len(results)));
364
        for vec::each(results) |r| {
365
            let ipv_prefix = match r {
B
Brian Anderson 已提交
366 367
              Ipv4(_) => ~"IPv4",
              Ipv6(_) => ~"IPv6"
368
            };
P
Paul Stansifer 已提交
369 370
            log(debug, fmt!("test_get_addr: result %s: '%s'",
                            ipv_prefix, format_addr(r)));
371
        }
372 373 374
        // 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;
375 376
    }
    #[test]
377
    #[ignore(reason = "valgrind says it's leaky")]
J
Jeff Olson 已提交
378
    fn test_ip_get_addr_bad_input() {
379
        let localhost_name = ~"sjkl234m,./sdf";
380 381 382
        let iotask = uv::global_loop::get();
        let ga_result = get_addr(localhost_name, iotask);
        assert result::is_err(ga_result);
383
    }
384
}