diff --git a/mk/crates.mk b/mk/crates.mk index e3534b6664cbe..69ddee72c4af1 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -37,7 +37,7 @@ # # DEPS_ # These lists are the dependencies of the that is to be built. -# Rust dependencies are listed bare (i.e. std, green) and native +# Rust dependencies are listed bare (i.e. std, extra, green) and native # dependencies have a "native:" prefix (i.e. native:sundown). All deps # will be built before the crate itself is built. # @@ -51,15 +51,15 @@ TARGET_CRATES := std green rustuv native flate arena glob term semver \ uuid serialize sync getopts collections num test time rand \ - workcache url log + workcache url log netsupport HOST_CRATES := syntax rustc rustdoc fourcc hexfloat CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc DEPS_std := native:rustrt native:compiler-rt native:backtrace DEPS_green := std rand native:context_switch -DEPS_rustuv := std native:uv native:uv_support -DEPS_native := std +DEPS_rustuv := std native:uv native:uv_support netsupport +DEPS_native := std netsupport DEPS_syntax := std term serialize collections log DEPS_rustc := syntax native:rustllvm flate arena serialize sync getopts \ collections time log @@ -84,6 +84,7 @@ DEPS_rand := std DEPS_url := std collections DEPS_workcache := std serialize collections log DEPS_log := std sync +DEPS_netsupport := std TOOL_DEPS_compiletest := test green rustuv getopts TOOL_DEPS_rustdoc := rustdoc native diff --git a/src/libnative/io/addrinfo.rs b/src/libnative/io/addrinfo.rs index ff617e5a230ed..3cb3a83c6d578 100644 --- a/src/libnative/io/addrinfo.rs +++ b/src/libnative/io/addrinfo.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate netsupport; + use ai = std::io::net::addrinfo; use std::c_str::CString; use std::cast; @@ -96,10 +98,8 @@ extern "system" { #[cfg(windows)] fn get_error(_: c_int) -> IoError { - use super::translate_error; - unsafe { - translate_error(WSAGetLastError() as i32, true) + netsupport::translate_error(WSAGetLastError() as i32, true) } } diff --git a/src/libnative/io/file_unix.rs b/src/libnative/io/file_unix.rs index 1d7938be22620..e5d924e92ec05 100644 --- a/src/libnative/io/file_unix.rs +++ b/src/libnative/io/file_unix.rs @@ -10,6 +10,8 @@ //! Blocking posix-based file I/O +extern crate netsupport; + use std::sync::arc::UnsafeArc; use std::c_str::CString; use std::io::IoError; @@ -62,7 +64,7 @@ impl FileDesc { if ret == 0 { Err(io::standard_error(io::EndOfFile)) } else if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(ret as uint) } @@ -75,7 +77,7 @@ impl FileDesc { } }); if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(()) } @@ -113,7 +115,7 @@ impl rtio::RtioFileStream for FileDesc { buf.len() as libc::size_t, offset as libc::off_t) as libc::c_int }) { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), n => Ok(n as int) } } @@ -131,7 +133,7 @@ impl rtio::RtioFileStream for FileDesc { }; let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) }; if n < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(n as u64) } @@ -139,7 +141,7 @@ impl rtio::RtioFileStream for FileDesc { fn tell(&self) -> Result { let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) }; if n < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(n as u64) } @@ -249,7 +251,7 @@ impl rtio::RtioFileStream for CFile { if ret == 0 { Err(io::standard_error(io::EndOfFile)) } else if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(ret as int) } @@ -263,7 +265,7 @@ impl rtio::RtioFileStream for CFile { } }); if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(()) } @@ -283,7 +285,7 @@ impl rtio::RtioFileStream for CFile { }; let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) }; if n < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(n as u64) } @@ -291,7 +293,7 @@ impl rtio::RtioFileStream for CFile { fn tell(&self) -> Result { let ret = unsafe { libc::ftell(self.file) }; if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(ret as u64) } @@ -330,7 +332,7 @@ pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess) }; match retry(|| unsafe { libc::open(path.with_ref(|p| p), flags, mode) }) { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), fd => Ok(FileDesc::new(fd, true)), } } @@ -378,7 +380,7 @@ pub fn readdir(p: &CString) -> IoResult<~[Path]> { assert_eq!(unsafe { closedir(dir_ptr) }, 0); Ok(prune(p, paths)) } else { - Err(super::last_error()) + Err(netsupport::last_error()) } } @@ -422,7 +424,7 @@ pub fn readlink(p: &CString) -> IoResult { libc::readlink(p, buf.as_ptr() as *mut libc::c_char, len as libc::size_t) as libc::c_int }) { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), n => { assert!(n > 0); unsafe { buf.set_len(n as uint); } @@ -495,7 +497,7 @@ pub fn stat(p: &CString) -> IoResult { let mut stat: libc::stat = unsafe { mem::uninit() }; match retry(|| unsafe { libc::stat(p.with_ref(|p| p), &mut stat) }) { 0 => Ok(mkstat(&stat, p)), - _ => Err(super::last_error()), + _ => Err(netsupport::last_error()), } } @@ -503,7 +505,7 @@ pub fn lstat(p: &CString) -> IoResult { let mut stat: libc::stat = unsafe { mem::uninit() }; match retry(|| unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) }) { 0 => Ok(mkstat(&stat, p)), - _ => Err(super::last_error()), + _ => Err(netsupport::last_error()), } } diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs index c5ae4f00017a9..dc09d7b544bee 100644 --- a/src/libnative/io/file_win32.rs +++ b/src/libnative/io/file_win32.rs @@ -10,6 +10,8 @@ //! Blocking win32-based file I/O +extern crate netsupport; + use std::c_str::CString; use std::cast; use std::io::IoError; @@ -63,7 +65,7 @@ impl FileDesc { if ret != 0 { Ok(read as uint) } else { - Err(super::last_error()) + Err(netsupport::last_error()) } } pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { @@ -80,7 +82,7 @@ impl FileDesc { remaining -= amt as uint; cur = unsafe { cur.offset(amt as int) }; } else { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } Ok(()) @@ -130,7 +132,7 @@ impl rtio::RtioFileStream for FileDesc { if ret != 0 { Ok(read as int) } else { - Err(super::last_error()) + Err(netsupport::last_error()) } } fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> Result<(), IoError> { @@ -151,7 +153,7 @@ impl rtio::RtioFileStream for FileDesc { cur = unsafe { cur.offset(amt as int) }; offset += amt as u64; } else { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } Ok(()) @@ -166,7 +168,7 @@ impl rtio::RtioFileStream for FileDesc { let mut newpos = 0; match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) { - 0 => Err(super::last_error()), + 0 => Err(netsupport::last_error()), _ => Ok(newpos as u64), } } @@ -190,7 +192,7 @@ impl rtio::RtioFileStream for FileDesc { let _ = try!(self.seek(offset, io::SeekSet)); let ret = unsafe { match libc::SetEndOfFile(self.handle()) { - 0 => Err(super::last_error()), + 0 => Err(netsupport::last_error()), _ => Ok(()) } }; @@ -300,14 +302,14 @@ pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess) ptr::mut_null()) }); if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE { - Err(super::last_error()) + Err(netsupport::last_error()) } else { let fd = unsafe { libc::open_osfhandle(handle as libc::intptr_t, flags) }; if fd < 0 { let _ = unsafe { libc::CloseHandle(handle) }; - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(FileDesc::new(fd, true)) } @@ -367,7 +369,7 @@ pub fn readdir(p: &CString) -> IoResult<~[Path]> { libc::free(wfd_ptr as *mut c_void); Ok(prune(p, paths)) } else { - Err(super::last_error()) + Err(netsupport::last_error()) } }) } @@ -421,7 +423,7 @@ pub fn readlink(p: &CString) -> IoResult { }) }; if handle as int == libc::INVALID_HANDLE_VALUE as int { - return Err(super::last_error()) + return Err(netsupport::last_error()) } // Specify (sz - 1) because the documentation states that it's the size // without the null pointer @@ -434,7 +436,7 @@ pub fn readlink(p: &CString) -> IoResult { let ret = match ret { Some(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))), Some(s) => Ok(Path::new(s)), - None => Err(super::last_error()), + None => Err(netsupport::last_error()), }; assert!(unsafe { libc::CloseHandle(handle) } != 0); return ret; @@ -495,7 +497,7 @@ pub fn stat(p: &CString) -> IoResult { as_utf16_p(p.as_str().unwrap(), |up| { match unsafe { libc::wstat(up, &mut stat) } { 0 => Ok(mkstat(&stat, p)), - _ => Err(super::last_error()), + _ => Err(netsupport::last_error()), } }) } diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 615ed80a64866..295b82045220d 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -21,19 +21,24 @@ //! play. The only dependencies of these modules are the normal system libraries //! that you would find on the respective platform. +extern crate netsupport; + use std::c_str::CString; use std::io; use std::io::IoError; use std::io::net::ip::SocketAddr; +use std::io::net::raw::Protocol; use std::io::process::ProcessConfig; use std::io::signal::Signum; use std::libc::c_int; use std::libc; +use std::num::from_i32; use std::os; use std::rt::rtio; use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket, RtioUnixListener, RtioPipe, RtioFileStream, RtioProcess, - RtioSignal, RtioTTY, CloseBehavior, RtioTimer}; + RtioSignal, RtioTTY, CloseBehavior, RtioTimer, + RtioRawSocket}; use ai = std::io::net::addrinfo; // Local re-exports @@ -86,76 +91,10 @@ fn unimpl() -> IoError { } } -fn translate_error(errno: i32, detail: bool) -> IoError { - #[cfg(windows)] - fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { - match errno { - libc::EOF => (io::EndOfFile, "end of file"), - libc::ERROR_NO_DATA => (io::BrokenPipe, "the pipe is being closed"), - libc::ERROR_FILE_NOT_FOUND => (io::FileNotFound, "file not found"), - libc::ERROR_INVALID_NAME => (io::InvalidInput, "invalid file name"), - libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"), - libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"), - libc::WSAEACCES => (io::PermissionDenied, "permission denied"), - libc::WSAEWOULDBLOCK => - (io::ResourceUnavailable, "resource temporarily unavailable"), - libc::WSAENOTCONN => (io::NotConnected, "not connected"), - libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"), - libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), - libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"), - libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"), - - // libuv maps this error code to EISDIR. we do too. if it is found - // to be incorrect, we can add in some more machinery to only - // return this message when ERROR_INVALID_FUNCTION after certain - // win32 calls. - libc::ERROR_INVALID_FUNCTION => (io::InvalidInput, - "illegal operation on a directory"), - - _ => (io::OtherIoError, "unknown error") - } - } - - #[cfg(not(windows))] - fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { - // FIXME: this should probably be a bit more descriptive... - match errno { - libc::EOF => (io::EndOfFile, "end of file"), - libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"), - libc::ECONNRESET => (io::ConnectionReset, "connection reset"), - libc::EPERM | libc::EACCES => - (io::PermissionDenied, "permission denied"), - libc::EPIPE => (io::BrokenPipe, "broken pipe"), - libc::ENOTCONN => (io::NotConnected, "not connected"), - libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"), - libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), - libc::EADDRINUSE => (io::ConnectionRefused, "address in use"), - libc::ENOENT => (io::FileNotFound, "no such file or directory"), - libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"), - - // These two constants can have the same value on some systems, but - // different values on others, so we can't use a match clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => - (io::ResourceUnavailable, "resource temporarily unavailable"), - - _ => (io::OtherIoError, "unknown error") - } - } - - let (kind, desc) = get_err(errno); - IoError { - kind: kind, - desc: desc, - detail: if detail {Some(os::last_os_error())} else {None}, - } -} - -fn last_error() -> IoError { translate_error(os::errno() as i32, true) } - // unix has nonzero values as errors fn mkerr_libc(ret: libc::c_int) -> IoResult<()> { if ret != 0 { - Err(last_error()) + Err(netsupport::last_error()) } else { Ok(()) } @@ -165,7 +104,7 @@ fn mkerr_libc(ret: libc::c_int) -> IoResult<()> { #[cfg(windows)] fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { if ret == 0 { - Err(last_error()) + Err(netsupport::last_error()) } else { Ok(()) } @@ -173,22 +112,24 @@ fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { #[cfg(windows)] #[inline] -fn retry(f: || -> libc::c_int) -> libc::c_int { +fn retry(f: || -> T) -> T { loop { - match f() { - -1 if os::errno() as int == libc::WSAEINTR as int => {} - n => return n, + let minus1: T = from_i32(-1).unwrap(); + let ret = f(); + if ret != minus1 || os::errno() as int != libc::WSAEINTR as int { + return ret } } } #[cfg(unix)] #[inline] -fn retry(f: || -> libc::c_int) -> libc::c_int { +fn retry(f: || -> T) -> T { loop { - match f() { - -1 if os::errno() as int == libc::EINTR as int => {} - n => return n, + let minus1: T = from_i32(-1).unwrap(); + let ret = f(); + if ret != minus1 || os::errno() as int != libc::EINTR as int { + return ret } } } @@ -245,6 +186,9 @@ impl rtio::IoFactory for IoFactory { hint: Option) -> IoResult<~[ai::Info]> { addrinfo::GetAddrInfoRequest::run(host, servname, hint) } + fn raw_socket_new(&mut self, protocol: Protocol) -> IoResult<~rtio::RtioRawSocket> { + net::RawSocket::new(protocol).map(|r| ~r as ~RtioRawSocket) + } // filesystem operations fn fs_from_raw_fd(&mut self, fd: c_int, diff --git a/src/libnative/io/net.rs b/src/libnative/io/net.rs index 6a71107294207..35b2acb9ce684 100644 --- a/src/libnative/io/net.rs +++ b/src/libnative/io/net.rs @@ -8,13 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate netsupport; + use std::cast; use std::io::net::ip; +use std::io::net::raw; use std::io; use std::libc; use std::mem; use std::rt::rtio; use std::sync::arc::UnsafeArc; +use std::intrinsics; use super::{IoResult, retry, keep_going}; @@ -25,68 +29,6 @@ use super::{IoResult, retry, keep_going}; #[cfg(windows)] pub type sock_t = libc::SOCKET; #[cfg(unix)] pub type sock_t = super::file::fd_t; -pub fn htons(u: u16) -> u16 { - mem::to_be16(u as i16) as u16 -} -pub fn ntohs(u: u16) -> u16 { - mem::from_be16(u as i16) as u16 -} - -enum InAddr { - InAddr(libc::in_addr), - In6Addr(libc::in6_addr), -} - -fn ip_to_inaddr(ip: ip::IpAddr) -> InAddr { - match ip { - ip::Ipv4Addr(a, b, c, d) => { - InAddr(libc::in_addr { - s_addr: (d as u32 << 24) | - (c as u32 << 16) | - (b as u32 << 8) | - (a as u32 << 0) - }) - } - ip::Ipv6Addr(a, b, c, d, e, f, g, h) => { - In6Addr(libc::in6_addr { - s6_addr: [ - htons(a), - htons(b), - htons(c), - htons(d), - htons(e), - htons(f), - htons(g), - htons(h), - ] - }) - } - } -} - -fn addr_to_sockaddr(addr: ip::SocketAddr) -> (libc::sockaddr_storage, uint) { - unsafe { - let storage: libc::sockaddr_storage = mem::init(); - let len = match ip_to_inaddr(addr.ip) { - InAddr(inaddr) => { - let storage: *mut libc::sockaddr_in = cast::transmute(&storage); - (*storage).sin_family = libc::AF_INET as libc::sa_family_t; - (*storage).sin_port = htons(addr.port); - (*storage).sin_addr = inaddr; - mem::size_of::() - } - In6Addr(inaddr) => { - let storage: *mut libc::sockaddr_in6 = cast::transmute(&storage); - (*storage).sin6_family = libc::AF_INET6 as libc::sa_family_t; - (*storage).sin6_port = htons(addr.port); - (*storage).sin6_addr = inaddr; - mem::size_of::() - } - }; - return (storage, len); - } -} - fn socket(addr: ip::SocketAddr, ty: libc::c_int) -> IoResult { unsafe { let fam = match addr.ip { @@ -94,7 +36,7 @@ fn socket(addr: ip::SocketAddr, ty: libc::c_int) -> IoResult { ip::Ipv6Addr(..) => libc::AF_INET6, }; match libc::socket(fam, ty, 0) { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), fd => Ok(fd), } } @@ -120,12 +62,12 @@ fn last_error() -> io::IoError { extern "system" { fn WSAGetLastError() -> libc::c_int; } - super::translate_error(unsafe { WSAGetLastError() }, true) + netsupport::translate_error(unsafe { WSAGetLastError() }, true) } #[cfg(not(windows))] fn last_error() -> io::IoError { - super::last_error() + netsupport::last_error() } #[cfg(windows)] unsafe fn close(sock: sock_t) { let _ = libc::closesocket(sock); } @@ -165,7 +107,7 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, let d = (addr >> 24) as u8; Ok(ip::SocketAddr { ip: ip::Ipv4Addr(a, b, c, d), - port: ntohs(storage.sin_port), + port: netsupport::ntohs(storage.sin_port), }) } libc::AF_INET6 => { @@ -173,17 +115,17 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, let storage: &libc::sockaddr_in6 = unsafe { cast::transmute(storage) }; - let a = ntohs(storage.sin6_addr.s6_addr[0]); - let b = ntohs(storage.sin6_addr.s6_addr[1]); - let c = ntohs(storage.sin6_addr.s6_addr[2]); - let d = ntohs(storage.sin6_addr.s6_addr[3]); - let e = ntohs(storage.sin6_addr.s6_addr[4]); - let f = ntohs(storage.sin6_addr.s6_addr[5]); - let g = ntohs(storage.sin6_addr.s6_addr[6]); - let h = ntohs(storage.sin6_addr.s6_addr[7]); + let a = netsupport::ntohs(storage.sin6_addr.s6_addr[0]); + let b = netsupport::ntohs(storage.sin6_addr.s6_addr[1]); + let c = netsupport::ntohs(storage.sin6_addr.s6_addr[2]); + let d = netsupport::ntohs(storage.sin6_addr.s6_addr[3]); + let e = netsupport::ntohs(storage.sin6_addr.s6_addr[4]); + let f = netsupport::ntohs(storage.sin6_addr.s6_addr[5]); + let g = netsupport::ntohs(storage.sin6_addr.s6_addr[6]); + let h = netsupport::ntohs(storage.sin6_addr.s6_addr[7]); Ok(ip::SocketAddr { ip: ip::Ipv6Addr(a, b, c, d, e, f, g, h), - port: ntohs(storage.sin6_port), + port: netsupport::ntohs(storage.sin6_port), }) } _ => { @@ -248,7 +190,7 @@ impl TcpStream { pub fn connect(addr: ip::SocketAddr) -> IoResult { unsafe { socket(addr, libc::SOCK_STREAM).and_then(|fd| { - let (addr, len) = addr_to_sockaddr(addr); + let (addr, len) = netsupport::addr_to_sockaddr(addr); let addrp = &addr as *libc::sockaddr_storage; let inner = Inner { fd: fd }; let ret = TcpStream { inner: UnsafeArc::new(inner) }; @@ -327,7 +269,7 @@ impl rtio::RtioTcpStream for TcpStream { 0) as i64 }); if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(()) } @@ -380,7 +322,7 @@ impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult { unsafe { socket(addr, libc::SOCK_STREAM).and_then(|fd| { - let (addr, len) = addr_to_sockaddr(addr); + let (addr, len) = netsupport::addr_to_sockaddr(addr); let addrp = &addr as *libc::sockaddr_storage; let inner = Inner { fd: fd }; let ret = TcpListener { inner: UnsafeArc::new(inner) }; @@ -481,7 +423,7 @@ impl UdpSocket { pub fn bind(addr: ip::SocketAddr) -> IoResult { unsafe { socket(addr, libc::SOCK_DGRAM).and_then(|fd| { - let (addr, len) = addr_to_sockaddr(addr); + let (addr, len) = netsupport::addr_to_sockaddr(addr); let addrp = &addr as *libc::sockaddr_storage; let inner = Inner { fd: fd }; let ret = UdpSocket { inner: UnsafeArc::new(inner) }; @@ -511,8 +453,8 @@ impl UdpSocket { pub fn set_membership(&mut self, addr: ip::IpAddr, opt: libc::c_int) -> IoResult<()> { - match ip_to_inaddr(addr) { - InAddr(addr) => { + match netsupport::ip_to_inaddr(addr) { + netsupport::InAddr(addr) => { let mreq = libc::ip_mreq { imr_multiaddr: addr, // interface == INADDR_ANY @@ -520,7 +462,7 @@ impl UdpSocket { }; setsockopt(self.fd(), libc::IPPROTO_IP, opt, mreq) } - In6Addr(addr) => { + netsupport::In6Addr(addr) => { let mreq = libc::ip6_mreq { ipv6mr_multiaddr: addr, ipv6mr_interface: 0, @@ -562,7 +504,7 @@ impl rtio::RtioUdpSocket for UdpSocket { } } fn sendto(&mut self, buf: &[u8], dst: ip::SocketAddr) -> IoResult<()> { - let (dst, len) = addr_to_sockaddr(dst); + let (dst, len) = netsupport::addr_to_sockaddr(dst); let dstp = &dst as *libc::sockaddr_storage; unsafe { let ret = retry(|| { @@ -634,3 +576,80 @@ impl rtio::RtioUdpSocket for UdpSocket { ~UdpSocket { inner: self.inner.clone() } as ~rtio::RtioUdpSocket } } + +//////////////////////////////////////////////////////////////////////////////// +/// Raw socket +//////////////////////////////////////////////////////////////////////////////// + +pub struct RawSocket { + priv fd: sock_t, +} + + +impl RawSocket { + pub fn new(protocol: raw::Protocol) -> IoResult + { + let (domain, typ, proto) = netsupport::protocol_to_libc(protocol); + let sock = unsafe { libc::socket(domain, typ, proto) }; + if sock == -1 { + return Err(netsupport::last_error()); + } + + let socket = RawSocket { fd: sock }; + + return Ok(socket); + } +} + +impl rtio::RtioRawSocket for RawSocket { + fn recvfrom(&mut self, buf: &mut [u8]) + -> IoResult<(uint, Option<~raw::NetworkAddress>)> + { + let mut caddr: libc::sockaddr_storage = unsafe { intrinsics::init() }; + let mut caddrlen = unsafe { + intrinsics::size_of::() + } as libc::socklen_t; + let len = unsafe { + let addr = &mut caddr as *mut libc::sockaddr_storage; + retry( || libc::recvfrom(self.fd, + buf.as_ptr() as *mut libc::c_void, + netsupport::net_buflen(buf), + 0, + addr as *mut libc::sockaddr, + &mut caddrlen)) + }; + if len == -1 { + return Err(netsupport::last_error()); + } + + return Ok((len as uint, netsupport::sockaddr_to_network_addr( + (&caddr as *libc::sockaddr_storage) as *libc::sockaddr, true) + )); + } + + fn sendto(&mut self, buf: &[u8], dst: ~raw::NetworkAddress) + -> IoResult + { + let (sockaddr, slen) = netsupport::network_addr_to_sockaddr(dst); + let addr = (&sockaddr as *libc::sockaddr_storage) as *libc::sockaddr; + let len = unsafe { + retry( || libc::sendto(self.fd, + buf.as_ptr() as *libc::c_void, + netsupport::net_buflen(buf), + 0, + addr, + slen as libc::socklen_t)) + }; + + return if len < 0 { + Err(netsupport::last_error()) + } else { + Ok(len as uint) + }; + } +} + +impl Drop for RawSocket { + fn drop(&mut self) { unsafe { close(self.fd) } } +} + diff --git a/src/libnative/io/pipe_unix.rs b/src/libnative/io/pipe_unix.rs index 9e81dc02cc596..554c760a9b34d 100644 --- a/src/libnative/io/pipe_unix.rs +++ b/src/libnative/io/pipe_unix.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate netsupport; + use std::c_str::CString; use std::cast; use std::io; @@ -22,7 +24,7 @@ use super::file::fd_t; fn unix_socket(ty: libc::c_int) -> IoResult { match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), fd => Ok(fd) } } @@ -84,7 +86,7 @@ fn connect(addr: &CString, ty: libc::c_int) -> IoResult { libc::connect(inner.fd, addrp as *libc::sockaddr, len as libc::socklen_t) }) { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), _ => Ok(inner) } } @@ -96,7 +98,7 @@ fn bind(addr: &CString, ty: libc::c_int) -> IoResult { match unsafe { libc::bind(inner.fd, addrp as *libc::sockaddr, len as libc::socklen_t) } { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), _ => Ok(inner) } } @@ -130,7 +132,7 @@ impl rtio::RtioPipe for UnixStream { if ret == 0 { Err(io::standard_error(io::EndOfFile)) } else if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(ret as uint) } @@ -144,7 +146,7 @@ impl rtio::RtioPipe for UnixStream { 0) as i64 }); if ret < 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(()) } @@ -191,7 +193,7 @@ impl UnixDatagram { storagep as *mut libc::sockaddr, &mut addrlen) as libc::c_int }); - if ret < 0 { return Err(super::last_error()) } + if ret < 0 { return Err(netsupport::last_error()) } sockaddr_to_unix(&storage, addrlen as uint).and_then(|addr| { Ok((ret as uint, addr)) }) @@ -209,7 +211,7 @@ impl UnixDatagram { len as libc::socklen_t) as libc::c_int }); match ret { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), n if n as uint != buf.len() => { Err(io::IoError { kind: io::OtherIoError, @@ -243,7 +245,7 @@ impl UnixListener { pub fn native_listen(self, backlog: int) -> IoResult { match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), _ => Ok(UnixAcceptor { listener: self }) } } @@ -272,7 +274,7 @@ impl UnixAcceptor { storagep as *mut libc::sockaddr, &mut size as *mut libc::socklen_t) as libc::c_int }) { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), fd => Ok(UnixStream { inner: UnsafeArc::new(Inner { fd: fd }) }) } } diff --git a/src/libnative/io/pipe_win32.rs b/src/libnative/io/pipe_win32.rs index e5e9592eb5ab5..ca267813da5af 100644 --- a/src/libnative/io/pipe_win32.rs +++ b/src/libnative/io/pipe_win32.rs @@ -84,6 +84,8 @@ //! the test suite passing (the suite is in libstd), and that's good enough for //! me! +extern crate netsupport; + use std::c_str::CString; use std::libc; use std::os::win32::as_utf16_p; @@ -105,7 +107,7 @@ impl Event { ptr::null()) }; if event as uint == 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(Event(event)) } @@ -226,7 +228,7 @@ impl UnixStream { ptr::mut_null()) }; return if ret == 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(UnixStream { inner: UnsafeArc::new(inner), @@ -243,14 +245,14 @@ impl UnixStream { // code of ERROR_PIPE_BUSY. let code = unsafe { libc::GetLastError() }; if code as int != libc::ERROR_PIPE_BUSY as int { - return Err(super::last_error()) + return Err(netsupport::last_error()) } // An example I found on microsoft's website used 20 seconds, // libuv uses 30 seconds, hence we make the obvious choice of // waiting for 25 seconds. if unsafe { libc::WaitNamedPipeW(p, 25000) } == 0 { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } }) @@ -286,10 +288,10 @@ impl rtio::RtioPipe for UnixStream { libc::TRUE) }; if ret == 0 { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } else { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } @@ -324,10 +326,10 @@ impl rtio::RtioPipe for UnixStream { libc::TRUE) }; if ret == 0 { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } else { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } offset += bytes_written as uint; @@ -361,7 +363,7 @@ impl UnixListener { as_utf16_p(addr.as_str().unwrap(), |p| { let ret = unsafe { pipe(p, true) }; if ret == libc::INVALID_HANDLE_VALUE as libc::HANDLE { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(UnixListener { handle: ret, name: addr.clone() }) } @@ -454,7 +456,7 @@ impl UnixAcceptor { } } if err != libc::ERROR_PIPE_CONNECTED as libc::DWORD { - return Err(super::last_error()) + return Err(netsupport::last_error()) } } @@ -465,7 +467,7 @@ impl UnixAcceptor { unsafe { pipe(p, false) } }); if new_handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE { - let ret = Err(super::last_error()); + let ret = Err(netsupport::last_error()); // If our disconnection fails, then there's not really a whole lot // that we can do, so fail the task. let err = unsafe { libc::DisconnectNamedPipe(handle) }; diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 6ac1f2b369244..8c8b4988f92af 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate netsupport; + use std::io; use std::libc::{pid_t, c_void, c_int}; use std::libc; @@ -161,7 +163,7 @@ unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { libc::PROCESS_QUERY_INFORMATION, libc::FALSE, pid as libc::DWORD); if handle.is_null() { - return Err(super::last_error()) + return Err(netsupport::last_error()) } let ret = match signal { // test for existence on signal 0 @@ -169,7 +171,7 @@ unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { let mut status = 0; let ret = libc::GetExitCodeProcess(handle, &mut status); if ret == 0 { - Err(super::last_error()) + Err(netsupport::last_error()) } else if status != libc::STILL_ACTIVE { Err(io::IoError { kind: io::OtherIoError, @@ -289,7 +291,7 @@ fn spawn_process_os(config: p::ProcessConfig, flags, envp, dirp, &mut si, &mut pi); if created == FALSE { - create_err = Some(super::last_error()); + create_err = Some(netsupport::last_error()); } }) }) @@ -465,7 +467,7 @@ fn spawn_process_os(config: p::ProcessConfig, (bytes[1] << 16) as i32 | (bytes[2] << 8) as i32 | (bytes[3] << 0) as i32; - Err(super::translate_error(errno, false)) + Err(netsupport::translate_error(errno, false)) } Err(e) => { assert!(e.kind == io::BrokenPipe || @@ -744,7 +746,7 @@ fn waitpid(pid: pid_t) -> p::ProcessExit { let mut status = 0 as c_int; match retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) { - -1 => fail!("unknown waitpid error: {}", super::last_error()), + -1 => fail!("unknown waitpid error: {}", netsupport::last_error()), _ => { if imp::WIFEXITED(status) { p::ExitStatus(imp::WEXITSTATUS(status) as int) diff --git a/src/libnative/io/timer_timerfd.rs b/src/libnative/io/timer_timerfd.rs index a8018bec0a6e3..8df99b895c3b9 100644 --- a/src/libnative/io/timer_timerfd.rs +++ b/src/libnative/io/timer_timerfd.rs @@ -28,6 +28,8 @@ //! //! As with timer_other, all units in this file are in units of millseconds. +extern crate netsupport; + use std::comm::Data; use std::libc; use std::ptr; @@ -175,7 +177,7 @@ impl Timer { pub fn new() -> IoResult { timer_helper::boot(helper); match unsafe { imp::timerfd_create(imp::CLOCK_MONOTONIC, 0) } { - -1 => Err(super::last_error()), + -1 => Err(netsupport::last_error()), n => Ok(Timer { fd: FileDesc::new(n, true), on_worker: false, }), } } diff --git a/src/libnative/io/timer_win32.rs b/src/libnative/io/timer_win32.rs index cdfe2e0d03372..182ee2c23a283 100644 --- a/src/libnative/io/timer_win32.rs +++ b/src/libnative/io/timer_win32.rs @@ -20,6 +20,8 @@ //! Other than that, the implementation is pretty straightforward in terms of //! the other two implementations of timers with nothing *that* new showing up. +extern crate netsupport; + use std::comm::Data; use std::libc; use std::ptr; @@ -98,7 +100,7 @@ impl Timer { imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null()) }; if obj.is_null() { - Err(super::last_error()) + Err(netsupport::last_error()) } else { Ok(Timer { obj: obj, on_worker: false, }) } diff --git a/src/libnetsupport/lib.rs b/src/libnetsupport/lib.rs new file mode 100644 index 0000000000000..1a9c713a2aa9a --- /dev/null +++ b/src/libnetsupport/lib.rs @@ -0,0 +1,413 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Networking support for code common to both librustuv and libnative + +#[crate_id = "netsupport#0.10-pre"]; +#[license = "MIT/ASL2"]; +#[crate_type = "rlib"]; +#[crate_type = "dylib"]; +#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "http://www.rust-lang.org/favicon.ico", + html_root_url = "http://static.rust-lang.org/doc/master")]; + +use std::cast; +use std::io; +use std::io::net::ip; +use std::io::net::ip::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::io::net::raw::{IpAddress, MacAddr, NetworkAddress, NetworkInterface}; +use std::io::net::raw; +use std::libc; +use std::mem; +use std::os; +use std::result::Result; +use std::vec_ng::Vec; + +#[cfg(not(windows))] use std::iter::Iterator; +#[cfg(not(windows))] use std::ptr; +#[cfg(not(windows))] use strraw = std::str::raw; + +pub fn htons(u: u16) -> u16 { + mem::to_be16(u as i16) as u16 +} +pub fn ntohs(u: u16) -> u16 { + mem::from_be16(u as i16) as u16 +} + +pub enum InAddr { + InAddr(libc::in_addr), + In6Addr(libc::in6_addr), +} + +pub fn ip_to_inaddr(ip: ip::IpAddr) -> InAddr { + match ip { + ip::Ipv4Addr(a, b, c, d) => { + InAddr(libc::in_addr { + s_addr: (d as u32 << 24) | + (c as u32 << 16) | + (b as u32 << 8) | + (a as u32 << 0) + }) + } + ip::Ipv6Addr(a, b, c, d, e, f, g, h) => { + In6Addr(libc::in6_addr { + s6_addr: [ + htons(a), + htons(b), + htons(c), + htons(d), + htons(e), + htons(f), + htons(g), + htons(h), + ] + }) + } + } +} + +pub fn addr_to_sockaddr(addr: ip::SocketAddr) -> (libc::sockaddr_storage, uint) { + unsafe { + let storage: libc::sockaddr_storage = mem::init(); + let len = match ip_to_inaddr(addr.ip) { + InAddr(inaddr) => { + let storage: *mut libc::sockaddr_in = cast::transmute(&storage); + (*storage).sin_family = libc::AF_INET as libc::sa_family_t; + (*storage).sin_port = htons(addr.port); + (*storage).sin_addr = inaddr; + mem::size_of::() + } + In6Addr(inaddr) => { + let storage: *mut libc::sockaddr_in6 = cast::transmute(&storage); + (*storage).sin6_family = libc::AF_INET6 as libc::sa_family_t; + (*storage).sin6_port = htons(addr.port); + (*storage).sin6_addr = inaddr; + mem::size_of::() + } + }; + return (storage, len); + } +} + +pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, + len: uint) -> Result { + match storage.ss_family as libc::c_int { + libc::AF_INET => { + assert!(len as uint >= mem::size_of::()); + let storage: &libc::sockaddr_in = unsafe { + cast::transmute(storage) + }; + let addr = storage.sin_addr.s_addr as u32; + let a = (addr >> 0) as u8; + let b = (addr >> 8) as u8; + let c = (addr >> 16) as u8; + let d = (addr >> 24) as u8; + Ok(ip::SocketAddr { + ip: ip::Ipv4Addr(a, b, c, d), + port: ntohs(storage.sin_port), + }) + } + libc::AF_INET6 => { + assert!(len as uint >= mem::size_of::()); + let storage: &libc::sockaddr_in6 = unsafe { + cast::transmute(storage) + }; + let a = ntohs(storage.sin6_addr.s6_addr[0]); + let b = ntohs(storage.sin6_addr.s6_addr[1]); + let c = ntohs(storage.sin6_addr.s6_addr[2]); + let d = ntohs(storage.sin6_addr.s6_addr[3]); + let e = ntohs(storage.sin6_addr.s6_addr[4]); + let f = ntohs(storage.sin6_addr.s6_addr[5]); + let g = ntohs(storage.sin6_addr.s6_addr[6]); + let h = ntohs(storage.sin6_addr.s6_addr[7]); + Ok(ip::SocketAddr { + ip: ip::Ipv6Addr(a, b, c, d, e, f, g, h), + port: ntohs(storage.sin6_port), + }) + } + _ => { + Err(io::standard_error(io::OtherIoError)) + } + } +} + +#[cfg(target_os = "linux")] +pub fn sockaddr_to_network_addr(sa: *libc::sockaddr, useLocal: bool) -> Option<~NetworkAddress> { + unsafe { + if (*sa).sa_family as libc::c_int == libc::AF_PACKET { + let sll: *libc::sockaddr_ll = cast::transmute(sa); + let ni = if useLocal { + let nis = get_network_interfaces(); + if nis.iter().filter(|x| x.index as i32 == (*sll).sll_ifindex).len() == 1 { + (*nis.iter() + .filter(|x| x.index as i32 == (*sll).sll_ifindex) + .next() + .unwrap()) + .clone() + } else { + sll_to_ni(*sll) + } + } else { + sll_to_ni(*sll) + }; + + return Some(~NetworkAddress(ni)); + } else { + return Some(~IpAddress(sockaddr_to_addr(cast::transmute(sa), + mem::size_of::() + ).unwrap().ip)); + } + } + + fn sll_to_ni(sll: libc::sockaddr_ll) -> ~NetworkInterface { + let mac = MacAddr(sll.sll_addr[0], sll.sll_addr[1], + sll.sll_addr[2], sll.sll_addr[3], + sll.sll_addr[4], sll.sll_addr[5]); + ~NetworkInterface { + name: ~"", + index: 0, + mac: Some(mac), + ipv4: None, + ipv6: None, + flags: 0 + } + } +} + +#[cfg(not(target_os = "linux"))] +pub fn sockaddr_to_network_addr(sa: *libc::sockaddr, _useLocal: bool) -> Option<~NetworkAddress> { + unsafe { + Some( + ~IpAddress( + sockaddr_to_addr(cast::transmute(sa), + mem::size_of::() + ).unwrap().ip) + ) + } +} + + +#[cfg(target_os = "linux")] +pub fn network_addr_to_sockaddr(na: ~NetworkAddress) -> (libc::sockaddr_storage, uint) { + unsafe { + match na { + ~IpAddress(ip) => addr_to_sockaddr(ip::SocketAddr { ip: ip, port : 0}), + //_ => (mem::init(), 0) + ~NetworkAddress(ni) => { + let mut storage: libc::sockaddr_storage = mem::init(); + let sll: &mut libc::sockaddr_ll = cast::transmute(&mut storage); + sll.sll_family = libc::AF_PACKET as libc::sa_family_t; + match ni.mac { + Some(MacAddr(a, b, c, d, e, f)) => sll.sll_addr = [a, b, c, d, e, f, 0, 0], + _ => () + } + sll.sll_halen = 6; + sll.sll_ifindex = ni.index as i32; + (storage, mem::size_of::()) + } + } + } +} + +#[cfg(not(target_os = "linux"))] +pub fn network_addr_to_sockaddr(na: ~NetworkAddress) -> (libc::sockaddr_storage, uint) { + match na { + ~IpAddress(ip) => addr_to_sockaddr(ip::SocketAddr { ip: ip, port : 0}), + _ => fail!("Layer 2 networking not supported on this OS") + } +} + +pub fn sockaddr_to_network_addrs(sa: *libc::sockaddr) + -> (Option, Option, Option) { + match sockaddr_to_network_addr(sa, false) { + Some(~IpAddress(ip@Ipv4Addr(..))) => (None, Some(ip), None), + Some(~IpAddress(ip@Ipv6Addr(..))) => (None, None, Some(ip)), + Some(~NetworkAddress(ni)) => (ni.mac, None, None), + None => (None, None, None) + } +} + +#[cfg(not(windows))] +pub fn get_network_interfaces() -> Vec<~NetworkInterface> { + let mut ifaces: Vec<~NetworkInterface> = Vec::new(); + unsafe { + let mut addrs: *libc::ifaddrs = mem::init(); + if libc::getifaddrs(&mut addrs) != 0 { + return ifaces; + } + let mut addr = addrs; + while addr != ptr::null() { + let name = strraw::from_c_str((*addr).ifa_name); + let (mac, ipv4, ipv6) = sockaddr_to_network_addrs((*addr).ifa_addr); + let ni = ~NetworkInterface { + name: name.clone(), + index: 0, + mac: mac, + ipv4: ipv4, + ipv6: ipv6, + flags: (*addr).ifa_flags + }; + let mut found: bool = false; + for iface in ifaces.mut_iter() { + if name == iface.name { + merge(iface, &ni); + found = true; + } + } + if !found { + ifaces.push(ni); + } + + addr = (*addr).ifa_next; + } + libc::freeifaddrs(addrs); + + for iface in ifaces.mut_iter() { + iface.index = libc::if_nametoindex(iface.name.to_c_str().unwrap()); + } + return ifaces; + } + + fn merge(old: &mut ~NetworkInterface, new: &~NetworkInterface) { + old.mac = match new.mac { + None => old.mac, + _ => new.mac + }; + old.ipv4 = match new.ipv4 { + None => old.ipv4, + _ => new.ipv4 + }; + old.ipv6 = match new.ipv6 { + None => old.ipv6, + _ => new.ipv6 + }; + old.flags = old.flags | new.flags; + } + +} + +#[cfg(windows)] +pub fn get_network_interfaces() -> Vec<~NetworkInterface> { + Vec::new() +} + + +#[cfg(target_os = "linux")] +pub fn protocol_to_libc(protocol: raw::Protocol) + -> (libc::c_int, libc::c_int, libc::c_int) { + let eth_p_all: u16 = htons(0x0003); + match protocol { + raw::DataLinkProtocol(raw::EthernetProtocol) + => (libc::AF_PACKET, libc::SOCK_RAW, eth_p_all as libc::c_int), + raw::DataLinkProtocol(raw::CookedEthernetProtocol(proto)) + => (libc::AF_PACKET, libc::SOCK_DGRAM, proto as libc::c_int), + raw::NetworkProtocol(raw::Ipv4NetworkProtocol) + => (libc::AF_INET, libc::SOCK_RAW, libc::IPPROTO_RAW), + raw::NetworkProtocol(raw::Ipv6NetworkProtocol) + => (libc::AF_INET6, libc::SOCK_RAW, libc::IPPROTO_RAW), + raw::TransportProtocol(raw::Ipv4TransportProtocol(proto)) + => (libc::AF_INET, libc::SOCK_RAW, proto as libc::c_int), + raw::TransportProtocol(raw::Ipv6TransportProtocol(proto)) + => (libc::AF_INET6, libc::SOCK_RAW, proto as libc::c_int) + } +} + +#[cfg(not(target_os = "linux"))] +pub fn protocol_to_libc(protocol: raw::Protocol) + -> (libc::c_int, libc::c_int, libc::c_int) { + match protocol { + raw::NetworkProtocol(raw::Ipv4NetworkProtocol) + => (libc::AF_INET, libc::SOCK_RAW, libc::IPPROTO_RAW), + raw::NetworkProtocol(raw::Ipv6NetworkProtocol) + => (libc::AF_INET6, libc::SOCK_RAW, libc::IPPROTO_RAW), + raw::TransportProtocol(raw::Ipv4TransportProtocol(proto)) + => (libc::AF_INET, libc::SOCK_RAW, proto as libc::c_int), + raw::TransportProtocol(raw::Ipv6TransportProtocol(proto)) + => (libc::AF_INET6, libc::SOCK_RAW, proto as libc::c_int), + _ => fail!("Layer 2 networking not supported on this OS") + } +} + +pub fn translate_error(errno: i32, detail: bool) -> io::IoError { + #[cfg(windows)] + fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { + match errno { + libc::EOF => (io::EndOfFile, "end of file"), + libc::ERROR_NO_DATA => (io::BrokenPipe, "the pipe is being closed"), + libc::ERROR_FILE_NOT_FOUND => (io::FileNotFound, "file not found"), + libc::ERROR_INVALID_NAME => (io::InvalidInput, "invalid file name"), + libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"), + libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"), + libc::WSAEACCES => (io::PermissionDenied, "permission denied"), + libc::WSAEWOULDBLOCK => + (io::ResourceUnavailable, "resource temporarily unavailable"), + libc::WSAENOTCONN => (io::NotConnected, "not connected"), + libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"), + libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), + libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"), + libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"), + + // libuv maps this error code to EISDIR. we do too. if it is found + // to be incorrect, we can add in some more machinery to only + // return this message when ERROR_INVALID_FUNCTION after certain + // win32 calls. + libc::ERROR_INVALID_FUNCTION => (io::InvalidInput, + "illegal operation on a directory"), + + _ => (io::OtherIoError, "unknown error") + } + } + + #[cfg(not(windows))] + fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { + // FIXME: this should probably be a bit more descriptive... + match errno { + libc::EOF => (io::EndOfFile, "end of file"), + libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"), + libc::ECONNRESET => (io::ConnectionReset, "connection reset"), + libc::EPERM | libc::EACCES => + (io::PermissionDenied, "permission denied"), + libc::EPIPE => (io::BrokenPipe, "broken pipe"), + libc::ENOTCONN => (io::NotConnected, "not connected"), + libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"), + libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), + libc::EADDRINUSE => (io::ConnectionRefused, "address in use"), + libc::ENOENT => (io::FileNotFound, "no such file or directory"), + libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"), + + // These two constants can have the same value on some systems, but + // different values on others, so we can't use a match clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => + (io::ResourceUnavailable, "resource temporarily unavailable"), + + _ => (io::OtherIoError, "unknown error") + } + } + + let (kind, desc) = get_err(errno); + io::IoError { + kind: kind, + desc: desc, + detail: if detail {Some(os::last_os_error())} else {None}, + } +} + +pub fn last_error() -> io::IoError { translate_error(os::errno() as i32, true) } + +#[cfg(windows)] +pub fn net_buflen(buf: &[u8]) -> i32 { + buf.len() as i32 +} + +#[cfg(not(windows))] +pub fn net_buflen(buf: &[u8]) -> u64 { + buf.len() as u64 +} + diff --git a/src/librustuv/addrinfo.rs b/src/librustuv/addrinfo.rs index 5d6af2969b8b3..cd9cbcfae484f 100644 --- a/src/librustuv/addrinfo.rs +++ b/src/librustuv/addrinfo.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate netsupport; + use ai = std::io::net::addrinfo; use std::cast; use std::libc; @@ -15,7 +17,6 @@ use std::libc::c_int; use std::ptr::null; use std::rt::task::BlockedTask; -use net; use super::{Loop, UvError, Request, wait_until_woken_after, wakeup}; use uvll; @@ -140,8 +141,8 @@ pub fn accum_addrinfo(addr: &Addrinfo) -> ~[ai::Info] { let mut addrs = ~[]; loop { - let rustaddr = net::sockaddr_to_addr(cast::transmute((*addr).ai_addr), - (*addr).ai_addrlen as uint); + let rustaddr = netsupport::sockaddr_to_addr(cast::transmute((*addr).ai_addr), + (*addr).ai_addrlen as uint).unwrap(); let mut flags = 0; each_ai_flag(|cval, aival| { diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs index 08d66e7270371..813073a4e7fdf 100644 --- a/src/librustuv/net.rs +++ b/src/librustuv/net.rs @@ -8,15 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +extern crate netsupport; + use std::cast; use std::io::IoError; use std::io::net::ip; +use std::io::net::raw; use std::libc::{size_t, ssize_t, c_int, c_void, c_uint}; use std::libc; use std::mem; +use std::os::errno; use std::ptr; use std::rt::rtio; use std::rt::task::BlockedTask; +use std::intrinsics; use access::Access; use homing::{HomingIO, HomeHandle}; @@ -31,93 +36,6 @@ use uvll; //////////////////////////////////////////////////////////////////////////////// /// Generic functions related to dealing with sockaddr things //////////////////////////////////////////////////////////////////////////////// - -pub fn htons(u: u16) -> u16 { mem::to_be16(u as i16) as u16 } -pub fn ntohs(u: u16) -> u16 { mem::from_be16(u as i16) as u16 } - -pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, - len: uint) -> ip::SocketAddr { - match storage.ss_family as c_int { - libc::AF_INET => { - assert!(len as uint >= mem::size_of::()); - let storage: &libc::sockaddr_in = unsafe { - cast::transmute(storage) - }; - let addr = storage.sin_addr.s_addr as u32; - let a = (addr >> 0) as u8; - let b = (addr >> 8) as u8; - let c = (addr >> 16) as u8; - let d = (addr >> 24) as u8; - ip::SocketAddr { - ip: ip::Ipv4Addr(a, b, c, d), - port: ntohs(storage.sin_port), - } - } - libc::AF_INET6 => { - assert!(len as uint >= mem::size_of::()); - let storage: &libc::sockaddr_in6 = unsafe { - cast::transmute(storage) - }; - let a = ntohs(storage.sin6_addr.s6_addr[0]); - let b = ntohs(storage.sin6_addr.s6_addr[1]); - let c = ntohs(storage.sin6_addr.s6_addr[2]); - let d = ntohs(storage.sin6_addr.s6_addr[3]); - let e = ntohs(storage.sin6_addr.s6_addr[4]); - let f = ntohs(storage.sin6_addr.s6_addr[5]); - let g = ntohs(storage.sin6_addr.s6_addr[6]); - let h = ntohs(storage.sin6_addr.s6_addr[7]); - ip::SocketAddr { - ip: ip::Ipv6Addr(a, b, c, d, e, f, g, h), - port: ntohs(storage.sin6_port), - } - } - n => { - fail!("unknown family {}", n); - } - } -} - -fn addr_to_sockaddr(addr: ip::SocketAddr) -> (libc::sockaddr_storage, uint) { - unsafe { - let mut storage: libc::sockaddr_storage = mem::init(); - let len = match addr.ip { - ip::Ipv4Addr(a, b, c, d) => { - let storage: &mut libc::sockaddr_in = - cast::transmute(&mut storage); - (*storage).sin_family = libc::AF_INET as libc::sa_family_t; - (*storage).sin_port = htons(addr.port); - (*storage).sin_addr = libc::in_addr { - s_addr: (d as u32 << 24) | - (c as u32 << 16) | - (b as u32 << 8) | - (a as u32 << 0) - }; - mem::size_of::() - } - ip::Ipv6Addr(a, b, c, d, e, f, g, h) => { - let storage: &mut libc::sockaddr_in6 = - cast::transmute(&mut storage); - storage.sin6_family = libc::AF_INET6 as libc::sa_family_t; - storage.sin6_port = htons(addr.port); - storage.sin6_addr = libc::in6_addr { - s6_addr: [ - htons(a), - htons(b), - htons(c), - htons(d), - htons(e), - htons(f), - htons(g), - htons(h), - ] - }; - mem::size_of::() - } - }; - return (storage, len); - } -} - enum SocketNameKind { TcpPeer, Tcp, @@ -140,7 +58,7 @@ fn socket_name(sk: SocketNameKind, match unsafe { getsockname(handle, sockaddr_p as *mut libc::sockaddr, &mut namelen) } { - 0 => Ok(sockaddr_to_addr(&sockaddr, namelen as uint)), + 0 => Ok(netsupport::sockaddr_to_addr(&sockaddr, namelen as uint).unwrap()), n => Err(uv_error_to_io_error(UvError(n))) } } @@ -204,7 +122,7 @@ impl TcpWatcher { struct Ctx { status: c_int, task: Option } let tcp = TcpWatcher::new(io); - let (addr, _len) = addr_to_sockaddr(address); + let (addr, _len) = netsupport::addr_to_sockaddr(address); let mut req = Request::new(uvll::UV_CONNECT); let result = unsafe { let addr_p = &addr as *libc::sockaddr_storage; @@ -369,7 +287,7 @@ impl TcpListener { outgoing: tx, incoming: rx, }; - let (addr, _len) = addr_to_sockaddr(address); + let (addr, _len) = netsupport::addr_to_sockaddr(address); let res = unsafe { let addr_p = &addr as *libc::sockaddr_storage; uvll::uv_tcp_bind(l.handle, addr_p as *libc::sockaddr) @@ -494,7 +412,7 @@ impl UdpWatcher { assert_eq!(unsafe { uvll::uv_udp_init(io.uv_loop(), udp.handle) }, 0); - let (addr, _len) = addr_to_sockaddr(address); + let (addr, _len) = netsupport::addr_to_sockaddr(address); let result = unsafe { let addr_p = &addr as *libc::sockaddr_storage; uvll::uv_udp_bind(udp.handle, addr_p as *libc::sockaddr, 0u32) @@ -593,7 +511,7 @@ impl rtio::RtioUdpSocket for UdpWatcher { None } else { let len = mem::size_of::(); - Some(sockaddr_to_addr(unsafe { cast::transmute(addr) }, len)) + Some(netsupport::sockaddr_to_addr(unsafe { cast::transmute(addr) }, len).unwrap()) }; cx.result = Some((nread, addr)); wakeup(&mut cx.task); @@ -609,7 +527,7 @@ impl rtio::RtioUdpSocket for UdpWatcher { let mut req = Request::new(uvll::UV_UDP_SEND); let buf = slice_to_uv_buf(buf); - let (addr, _len) = addr_to_sockaddr(dst); + let (addr, _len) = netsupport::addr_to_sockaddr(dst); let result = unsafe { let addr_p = &addr as *libc::sockaddr_storage; uvll::uv_udp_send(req.handle, self.handle, [buf], @@ -730,6 +648,236 @@ impl Drop for UdpWatcher { } } +//////////////////////////////////////////////////////////////////////////////// +/// Raw socket implementation +//////////////////////////////////////////////////////////////////////////////// + +pub struct RawSocketWatcher { + handle: *uvll::uv_poll_t, + socket: uvll::uv_os_socket_t, + home: HomeHandle, +} + +#[cfg(windows)] +fn make_nonblocking(socket: libc::SOCKET) -> Option { + let one: libc::c_ulong = 1; + if unsafe { libc::ioctlsocket(socket, libc::FIONBIO, &one as *libc::c_ulong) } != 0 { + Some(netsupport::last_error()) + } else { + None + } +} + +#[cfg(not(windows))] +fn make_nonblocking(socket: c_int) -> Option { + let flags = unsafe { libc::fcntl(socket, libc::F_GETFL, 0) }; + if flags == -1 { + return Some(netsupport::last_error()); + } + if unsafe { libc::fcntl(socket, libc::F_SETFL, flags | libc::O_NONBLOCK) } == -1 { + return Some(netsupport::last_error()); + } + return None; +} + +impl RawSocketWatcher { + pub fn new(io: &mut UvIoFactory, protocol: raw::Protocol) + -> Result + { + let (domain, typ, proto) = netsupport::protocol_to_libc(protocol); + let handle = unsafe { uvll::malloc_handle(uvll::UV_POLL) }; + let socket = unsafe { libc::socket(domain, typ, proto) }; + if socket == -1 { + return Err(netsupport::last_error()); + } + + let raw = RawSocketWatcher { + handle: handle, + home: io.make_handle(), + socket: socket + }; + + // Make socket non-blocking - required for libuv + match make_nonblocking(raw.socket) { + Some(e) => return Err(e), + None => () + } + + assert_eq!(unsafe { + uvll::uv_poll_init_socket(io.uv_loop(), raw.handle, raw.socket) + }, 0); + return Ok(raw); + } +} + +impl UvHandle for RawSocketWatcher { + fn uv_handle(&self) -> *uvll::uv_poll_t { self.handle } +} + +impl Drop for RawSocketWatcher { + fn drop(&mut self) { + let _m = self.fire_homing_missile(); + self.close(); + } +} + +impl HomingIO for RawSocketWatcher { + fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } +} + +impl rtio::RtioRawSocket for RawSocketWatcher { + fn recvfrom(&mut self, buf: &mut [u8]) + -> Result<(uint, Option<~raw::NetworkAddress>), IoError> + { + struct Ctx<'b> { + task: Option, + buf: &'b [u8], + result: Option<(ssize_t, Option<~raw::NetworkAddress>)>, + socket: Option, + } + let _m = self.fire_homing_missile(); + let a = match unsafe { + uvll::uv_poll_start(self.handle, uvll::UV_READABLE as c_int, recv_cb) + } { + 0 => { + let mut cx = Ctx { + task: None, + buf: buf, + result: None, + socket: Some(self.socket), + }; + wait_until_woken_after(&mut cx.task, &self.uv_loop(), || { + unsafe { uvll::set_data_for_uv_handle(self.handle, &cx) } + }); + match cx.result.take_unwrap() { + (n, _) if n < 0 => + Err(netsupport::translate_error(n as i32, true)), + (n, addr) => Ok((n as uint, Some(addr.unwrap()))) + } + } + n => Err(uv_error_to_io_error(UvError(n))) + }; + return a; + + extern fn recv_cb(handle: *uvll::uv_poll_t, status: c_int, events: c_int) { + assert!((events & (uvll::UV_READABLE as c_int)) != 0); + let cx: &mut Ctx = unsafe { + cast::transmute(uvll::get_data_for_uv_handle(handle)) + }; + + if status < 0 { + cx.result = Some((status as ssize_t, None)); + wakeup(&mut cx.task); + return; + } + + unsafe { + assert_eq!(uvll::uv_poll_stop(handle), 0) + } + + let mut caddr = unsafe { intrinsics::init::() }; + let mut caddrlen = unsafe { + intrinsics::size_of::() + } as libc::socklen_t; + let len = match cx.socket { + Some(sock) => unsafe { + let addr = &mut caddr as *mut libc::sockaddr_storage; + libc::recvfrom(sock, + cx.buf.as_ptr() as *mut c_void, + netsupport::net_buflen(cx.buf), + 0, + addr as *mut libc::sockaddr, + &mut caddrlen) + }, + _ => -1 + }; + if len == -1 { + cx.result = Some((-errno() as ssize_t, None)); + wakeup(&mut cx.task); + return; + } + let addr = netsupport::sockaddr_to_network_addr( + (&caddr as *libc::sockaddr_storage) as *libc::sockaddr, true + ); + cx.result = Some((len as ssize_t, addr)); + + wakeup(&mut cx.task); + } + } + + fn sendto(&mut self, buf: &[u8], dst: ~raw::NetworkAddress) + -> Result + { + struct Ctx<'b> { + task: Option, + buf: &'b [u8], + result: Option>, + socket: Option, + addr: ~raw::NetworkAddress, + } + let _m = self.fire_homing_missile(); + + let a = match unsafe { + uvll::uv_poll_start(self.handle, uvll::UV_WRITABLE as c_int, send_cb) + } { + 0 => { + let mut cx = Ctx { + task: None, + buf: buf, + result: None, + socket: Some(self.socket), + addr: dst + }; + wait_until_woken_after(&mut cx.task, &self.uv_loop(), || { + unsafe { uvll::set_data_for_uv_handle(self.handle, &cx) } + }); + cx.result.unwrap() + } + n => Err(uv_error_to_io_error(UvError(n))) + }; + return a; + + extern fn send_cb(handle: *uvll::uv_poll_t, status: c_int, events: c_int) { + assert!((events & (uvll::UV_WRITABLE as c_int)) != 0); + let cx: &mut Ctx = unsafe { + cast::transmute(uvll::get_data_for_uv_handle(handle)) + }; + if status < 0 { + cx.result = Some(Err(uv_error_to_io_error(UvError(status)))); + wakeup(&mut cx.task); + return; + } + + unsafe { + assert_eq!(uvll::uv_poll_stop(handle), 0) + } + + let len = match cx.socket { + Some(sock) => { + let (addr, len) = netsupport::network_addr_to_sockaddr(cx.addr.clone()); + unsafe { + libc::sendto(sock, + cx.buf.as_ptr() as *c_void, + netsupport::net_buflen(cx.buf), + 0, + (&addr as *libc::sockaddr_storage) as *libc::sockaddr, + len as libc::socklen_t) + } + }, + _ => -1 + }; + + cx.result = if len < 0 { + Some(Err(netsupport::last_error())) + } else { + Some(Ok(len as uint)) + }; + + wakeup(&mut cx.task); + } + } +} + #[cfg(test)] mod test { use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioTcpAcceptor, diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs index e4326fe36bfed..ca001ad0123be 100644 --- a/src/librustuv/uvio.rs +++ b/src/librustuv/uvio.rs @@ -12,6 +12,7 @@ use std::c_str::CString; use std::cast; use std::io::IoError; use std::io::net::ip::SocketAddr; +use std::io::net::raw::{Protocol}; use std::io::process::ProcessConfig; use std::io::signal::Signum; use std::io::{FileMode, FileAccess, Open, Append, Truncate, Read, Write, @@ -36,7 +37,7 @@ use file::{FsRequest, FileWatcher}; use queue::QueuePool; use homing::HomeHandle; use idle::IdleWatcher; -use net::{TcpWatcher, TcpListener, UdpWatcher}; +use net::{RawSocketWatcher, TcpWatcher, TcpListener, UdpWatcher}; use pipe::{PipeWatcher, PipeListener}; use process::Process; use signal::SignalWatcher; @@ -179,6 +180,11 @@ impl IoFactory for UvIoFactory { r.map_err(uv_error_to_io_error) } + fn raw_socket_new(&mut self, protocol: Protocol) -> Result<~rtio::RtioRawSocket, IoError> { + RawSocketWatcher::new(self, protocol) + .map(|rsw| ~rsw as ~rtio::RtioRawSocket) + } + fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior) -> ~rtio::RtioFileStream { ~FileWatcher::new(self, fd, close) as ~rtio::RtioFileStream diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs index b9b7ed13cc1b1..fb6d853f2dc19 100644 --- a/src/librustuv/uvll.rs +++ b/src/librustuv/uvll.rs @@ -104,6 +104,9 @@ pub struct uv_buf_t { len: uv_buf_len_t, } +#[cfg(unix)] +pub type uv_os_socket_t = c_int; + // see libuv/include/uv-win.h #[cfg(windows)] pub struct uv_buf_t { @@ -111,6 +114,9 @@ pub struct uv_buf_t { base: *u8, } +#[cfg(windows)] +pub type uv_os_socket_t = libc::SOCKET; + #[repr(C)] pub enum uv_run_mode { RUN_DEFAULT = 0, @@ -118,6 +124,12 @@ pub enum uv_run_mode { RUN_NOWAIT, } +#[repr(C)] +pub enum uv_poll_event { + UV_READABLE = 1, + UV_WRITABLE = 2, +} + pub struct uv_process_options_t { exit_cb: uv_exit_cb, file: *libc::c_char, @@ -144,6 +156,7 @@ pub type uv_loop_t = c_void; pub type uv_idle_t = c_void; pub type uv_tcp_t = c_void; pub type uv_udp_t = c_void; +pub type uv_poll_t = c_void; pub type uv_connect_t = c_void; pub type uv_connection_t = c_void; pub type uv_write_t = c_void; @@ -228,6 +241,9 @@ pub type uv_udp_recv_cb = extern "C" fn(handle: *uv_udp_t, addr: *sockaddr, flags: c_uint); pub type uv_close_cb = extern "C" fn(handle: *uv_handle_t); +pub type uv_poll_cb = extern "C" fn(handle: *uv_poll_t, + status: c_int, + events: c_int); pub type uv_walk_cb = extern "C" fn(handle: *uv_handle_t, arg: *c_void); pub type uv_async_cb = extern "C" fn(handle: *uv_async_t, @@ -638,6 +654,11 @@ extern { pub fn uv_fs_lstat(handle: *uv_loop_t, req: *uv_fs_t, file: *c_char, cb: uv_fs_cb) -> c_int; + // poll bindings + pub fn uv_poll_init_socket(l: *uv_loop_t, h: *uv_poll_t, s: uv_os_socket_t) -> c_int; + pub fn uv_poll_start(h: *uv_poll_t, events: c_int, cb: uv_poll_cb) -> c_int; + pub fn uv_poll_stop(h: *uv_poll_t) -> c_int; + // getaddrinfo pub fn uv_getaddrinfo(loop_: *uv_loop_t, req: *uv_getaddrinfo_t, getaddrinfo_cb: uv_getaddrinfo_cb, diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs index 1939d6537521f..f71adb80bccfc 100644 --- a/src/libstd/io/net/mod.rs +++ b/src/libstd/io/net/mod.rs @@ -16,5 +16,6 @@ pub mod addrinfo; pub mod tcp; pub mod udp; pub mod ip; +pub mod raw; // FIXME(#12093) - this should not be called unix pub mod unix; diff --git a/src/libstd/io/net/raw.rs b/src/libstd/io/net/raw.rs new file mode 100644 index 0000000000000..cec5cddb6c7ed --- /dev/null +++ b/src/libstd/io/net/raw.rs @@ -0,0 +1,1692 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Raw layer two to four network connections +//! +//! This module enables the creation of raw network sockets to send and receive +//! packets at OSI layers two, three and four. + +#[deny(missing_doc)]; + +use container::Container; +use fmt; +use io::net::ip::{IpAddr, Ipv4Addr, Ipv6Addr}; +use io::{IoResult}; +use iter::Iterator; +use libc; +use option::{Option}; +use rt::rtio::{IoFactory, LocalIo, RtioRawSocket}; +use clone::Clone; +use vec::{Vector, ImmutableVector}; + +#[cfg(test)] +use vec::MutableVector; + +/// A structure which represents a raw socket +/// +/// # Example +/// +/// Simple packet logger. +/// +/// ```rust +/// use std::io::net::raw::{DataLinkProtocol, EthernetProtocol, RawSocket}; +/// +/// let proto = DataLinkProtocol(EthernetProtocol); +/// let mut socket = RawSocket::new(proto).unwrap(); +/// loop { +/// let mut buf = [0u8, ..4096]; +/// let (len, addr) = socket.recvfrom(buf.as_mut_slice()).unwrap(); +/// handle_packet(addr, buf.slice_to(len)); +/// } +/// ``` +pub struct RawSocket { + priv obj: ~RtioRawSocket +} + +impl RawSocket { + /// Create a new RawSocket using the specified protocol + /// + /// If no error is encountered, then Ok(socket) is returned. + pub fn new(protocol: Protocol) -> IoResult { + LocalIo::maybe_raise(|io| { + io.raw_socket_new(protocol).map(|s| RawSocket { obj: s }) + }) + } + + /// Receive data from a socket + /// + /// Returns Ok(length_of_received_data, network_address) on success. + pub fn recvfrom(&mut self, buf: &mut [u8]) -> IoResult<(uint, Option<~NetworkAddress>)> { + self.obj.recvfrom(buf) + } + + /// Send data on the socket + /// + /// Returns length of sent data on success + pub fn sendto(&mut self, buf: &[u8], dst: ~NetworkAddress) -> IoResult { + self.obj.sendto(buf, dst) + } +} + + +/// Represents a network interface and its associated addresses +#[deriving(Clone, Eq, Show)] +pub struct NetworkInterface { + /// The name of the interface + name: ~str, + /// The interface index (operating system specific) + index: u32, + /// A MAC address for the interface + mac: Option, + /// An IPv4 address for the interface + ipv4: Option, + /// An IPv6 address for the interface + ipv6: Option, + /// Operating system specific flags for the interface + flags: u32, +} + +impl NetworkInterface { + /// Retreive the MAC address associated with the interface + pub fn mac_address(&self) -> MacAddr { + self.mac.unwrap() + } + + /// Is the interface a loopback interface? + pub fn is_loopback(&self) -> bool { + self.flags & (libc::IFF_LOOPBACK as u32) != 0 + } +} + +/// Represents a network address. This is either an IP address or a network +/// interface +#[deriving(Clone, Eq, Show)] +pub enum NetworkAddress { + /// An IP address + IpAddress(IpAddr), + /// A network interface + NetworkAddress(~NetworkInterface) +} + +/// A MAC address +#[deriving(Eq, Clone)] +pub enum MacAddr { + /// A MAC address + MacAddr(u8, u8, u8, u8, u8, u8) +} + +impl fmt::Show for MacAddr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + MacAddr(a, b, c, d, e, f) => + write!(fmt.buf, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + a, b, c, d, e, f) + } + } +} + +/// Represents a generic network packet +pub trait Packet { + /// Retreive the underlying buffer for the packet + fn packet<'p>(&'p self) -> &'p [u8]; + /// Retreive the offset into the buffer that the packet starts at + fn offset(&self) -> uint; +} + +/// A structure which represents an Ethernet header +pub struct EthernetHeader<'p> { + priv packet: &'p [u8], + priv offset: uint +} + +/// A structure representing an Ethernet header which can be mutated +pub struct MutableEthernetHeader<'p> { + priv packet: &'p mut [u8], + priv offset: uint +} + +impl<'p> Packet for EthernetHeader<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +impl<'p> Packet for MutableEthernetHeader<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet.as_slice() } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +/// A trait implemented by anything which provides the ability to retrieve +/// fields of an Ethernet packet +pub trait EthernetPacket : Packet { + /// Get the source address for an Ethernet packet + fn get_source(&self) -> MacAddr { + MacAddr( + self.packet()[self.offset() + 6], + self.packet()[self.offset() + 7], + self.packet()[self.offset() + 8], + self.packet()[self.offset() + 9], + self.packet()[self.offset() + 10], + self.packet()[self.offset() + 11] + ) + } + + /// Get the destination address for an Ethernet packet + fn get_destination(&self) -> MacAddr { + MacAddr( + self.packet()[self.offset() + 0], + self.packet()[self.offset() + 1], + self.packet()[self.offset() + 2], + self.packet()[self.offset() + 3], + self.packet()[self.offset() + 4], + self.packet()[self.offset() + 5] + ) + } + + /// Get the Ethertype field of an Ethernet packet + fn get_ethertype(&self) -> u16 { + (self.packet()[self.offset() + 12] as u16 << 8) | (self.packet()[self.offset() + 13] as u16) + } +} + +impl<'p> EthernetPacket for EthernetHeader<'p> {} +impl<'p> EthernetPacket for MutableEthernetHeader<'p> {} + +impl<'p> EthernetHeader<'p> { + /// Construct a new Ethernet header backed by the given buffer with the + /// provided offset + pub fn new(packet: &'p [u8], offset: uint) -> EthernetHeader<'p> { + EthernetHeader { packet: packet, offset: offset } + } +} + +impl<'p> MutableEthernetHeader<'p> { + /// Construct a new mutable Ethernet header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p mut [u8], offset: uint) -> MutableEthernetHeader<'p> { + MutableEthernetHeader { packet: packet, offset: offset } + } + + /// Set the source address for an Ethernet packet + pub fn set_source(&mut self, mac: MacAddr) { + match mac { + MacAddr(a, b, c, d, e, f) => { + self.packet[self.offset + 6] = a; + self.packet[self.offset + 7] = b; + self.packet[self.offset + 8] = c; + self.packet[self.offset + 9] = d; + self.packet[self.offset + 10] = e; + self.packet[self.offset + 11] = f; + } + } + } + + /// Set the destination address for an Ethernet packet + pub fn set_destination(&mut self, mac: MacAddr) { + match mac { + MacAddr(a, b, c, d, e, f) => { + self.packet[self.offset + 0] = a; + self.packet[self.offset + 1] = b; + self.packet[self.offset + 2] = c; + self.packet[self.offset + 3] = d; + self.packet[self.offset + 4] = e; + self.packet[self.offset + 5] = f; + } + } + } + + /// Set the Ethertype for an Ethernet packet + pub fn set_ethertype(&mut self, ethertype: u16) { + self.packet[self.offset + 12] = (ethertype >> 8) as u8; + self.packet[self.offset + 13] = (ethertype & 0xFF) as u8; + } +} + +#[test] +fn ethernet_header_test() { + let mut packet = [0u8, ..14]; + { + let mut ethernetHeader = MutableEthernetHeader::new(packet.as_mut_slice(), 0); + + let source = MacAddr(0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc); + ethernetHeader.set_source(source); + assert_eq!(ethernetHeader.get_source(), source); + + let dest = MacAddr(0xde, 0xf0, 0x12, 0x34, 0x45, 0x67); + ethernetHeader.set_destination(dest); + assert_eq!(ethernetHeader.get_destination(), dest); + + ethernetHeader.set_ethertype(EtherTypes::Ipv6); + assert_eq!(ethernetHeader.get_ethertype(), EtherTypes::Ipv6); + } + + let refPacket = [0xde, 0xf0, 0x12, 0x34, 0x45, 0x67, /* destination */ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, /* source */ + 0x86, 0xdd /* ethertype */]; + assert_eq!(refPacket.as_slice(), packet.as_slice()); +} + +/// Structure representing an IPv4 header +pub struct Ipv4Header<'p> { + priv packet: &'p [u8], + priv offset: uint +} + +/// Structure representing a mutable IPv4 header +pub struct MutableIpv4Header<'p> { + priv packet: &'p mut [u8], + priv offset: uint +} + +impl<'p> Packet for Ipv4Header<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +impl<'p> Packet for MutableIpv4Header<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet.as_slice() } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +/// Trait implemented by anything which provides an interface to read IPv4 +/// packets +pub trait Ipv4Packet : Packet { + /// Get the version of the IPv4 packet. Should return 4. + fn get_version(&self) -> u8 { + self.packet()[self.offset()] >> 4 + } + + /// Get the header length field of the packet + fn get_header_length(&self) -> u8 { + self.packet()[self.offset()] & 0xF + } + + /// Get the DSCP field of the packet + fn get_dscp(&self) -> u8 { + (self.packet()[self.offset() + 1] & 0xFC) >> 2 + } + + /// Get the ECN field of the packet + fn get_ecn(&self) -> u8 { + self.packet()[self.offset() + 1] & 3 + } + + /// Get the total length of the packet + fn get_total_length(&self) -> u16 { + let b1 = self.packet()[self.offset() + 2] as u16 << 8; + let b2 = self.packet()[self.offset() + 3] as u16; + b1 | b2 + } + + /// Get the identification field for the packet + fn get_identification(&self) -> u16 { + let b1 = self.packet()[self.offset() + 4] as u16 << 8; + let b2 = self.packet()[self.offset() + 5] as u16; + b1 | b2 + } + + /// Get the flags for the packet + fn get_flags(&self) -> u8 { + self.packet()[self.offset() + 6] >> 5 + } + + /// Get the fragment offset for the packet + fn get_fragment_offset(&self) -> u16 { + let b1 = (self.packet()[self.offset() + 6] & 0x1F) as u16 << 8; + let b2 = self.packet()[self.offset() + 7] as u16; + b1 | b2 + } + + /// Get the TTL for the packet + fn get_ttl(&self) -> u8 { + self.packet()[self.offset() + 8] + } + + /// Get the next level protocol for the packet + fn get_next_level_protocol(&self) -> IpNextHeaderProtocol { + self.packet()[self.offset() + 9] + } + + /// Get the checksum field for the packet + fn get_checksum(&self) -> u16 { + let cs1 = self.packet()[self.offset() + 10] as u16 << 8; + let cs2 = self.packet()[self.offset() + 11] as u16; + cs1 | cs2 + } + + /// Get the source IP address for the packet + fn get_source(&self) -> IpAddr { + Ipv4Addr(self.packet()[self.offset() + 12], + self.packet()[self.offset() + 13], + self.packet()[self.offset() + 14], + self.packet()[self.offset() + 15]) + } + + /// Get the destination field for the packet + fn get_destination(&self) -> IpAddr { + Ipv4Addr(self.packet()[self.offset() + 16], + self.packet()[self.offset() + 17], + self.packet()[self.offset() + 18], + self.packet()[self.offset() + 19]) + } + + /// Calculate the checksum for the packet + fn calculate_checksum(&mut self) -> u16 { + let len = self.offset() + self.get_header_length() as uint * 4; + let mut sum = 0u32; + let mut i = self.offset(); + while i < len { + let word = self.packet()[i] as u32 << 8 | self.packet()[i + 1] as u32; + sum = sum + word; + i = i + 2; + } + while sum >> 16 != 0 { + sum = (sum >> 16) + (sum & 0xFFFF); + } + return !sum as u16; + } +} + +impl<'p> Ipv4Packet for Ipv4Header<'p> {} +impl<'p> Ipv4Packet for MutableIpv4Header<'p> {} + +impl<'p> Ipv4Header<'p> { + /// Construct a new IPv4 header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p [u8], offset: uint) -> Ipv4Header<'p> { + Ipv4Header { packet: packet, offset: offset } + } +} +impl<'p> MutableIpv4Header<'p> { + /// Construct a new mutable IPv4 header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p mut [u8], offset: uint) -> MutableIpv4Header<'p> { + MutableIpv4Header { packet: packet, offset: offset } + } + + /// Set the version field for the packet + pub fn set_version(&mut self, version: u8) { + let ver = version << 4; + self.packet[self.offset] = (self.packet[self.offset] & 0x0F) | ver; + } + + /// Set the header length field for the packet + pub fn set_header_length(&mut self, ihl: u8) { + let len = ihl & 0xF; + self.packet[self.offset] = (self.packet[self.offset] & 0xF0) | len; + } + + /// Set the DSCP field for the packet + pub fn set_dscp(&mut self, dscp: u8) { + let cp = dscp & 0xFC; + self.packet[self.offset + 1] = (self.packet[self.offset + 1] & 3) | (cp << 2); + } + + /// Set the ECN field for the packet + pub fn set_ecn(&mut self, ecn: u8) { + let cn = ecn & 3; + self.packet[self.offset + 1] = (self.packet[self.offset + 1] & 0xFC) | cn; + } + + /// Set the total length field for the packet + pub fn set_total_length(&mut self, len: u16) { + self.packet[self.offset + 2] = (len >> 8) as u8; + self.packet[self.offset + 3] = (len & 0xFF) as u8; + } + + /// Set the identification field for the packet + pub fn set_identification(&mut self, identification: u16) { + self.packet[self.offset + 4] = (identification >> 8) as u8; + self.packet[self.offset + 5] = (identification & 0x00FF) as u8; + } + + /// Set the flags field for the packet + pub fn set_flags(&mut self, flags: u8) { + let fs = (flags & 7) << 5; + self.packet[self.offset + 6] = (self.packet[self.offset + 6] & 0x1F) | fs; + } + + /// Set the fragment offset field for the packet + pub fn set_fragment_offset(&mut self, offset: u16) { + let fo = offset & 0x1FFF; + self.packet[self.offset + 6] = (self.packet[self.offset + 6] & 0xE0) | + ((fo & 0xFF00) >> 8) as u8; + self.packet[self.offset + 7] = (fo & 0xFF) as u8; + } + + /// Set the TTL field for the packet + pub fn set_ttl(&mut self, ttl: u8) { + self.packet[self.offset + 8] = ttl; + } + + /// Set the next level protocol field for the packet + pub fn set_next_level_protocol(&mut self, protocol: IpNextHeaderProtocol) { + self.packet[self.offset + 9] = protocol; + } + + /// Set the checksum field for the packet + pub fn set_checksum(&mut self, checksum: u16) { + let cs1 = ((checksum & 0xFF00) >> 8) as u8; + let cs2 = (checksum & 0x00FF) as u8; + self.packet[self.offset + 10] = cs1; + self.packet[self.offset + 11] = cs2; + } + + /// Set the source address for the packet + pub fn set_source(&mut self, ip: IpAddr) { + match ip { + Ipv4Addr(a, b, c, d) => { + self.packet[self.offset + 12] = a; + self.packet[self.offset + 13] = b; + self.packet[self.offset + 14] = c; + self.packet[self.offset + 15] = d; + }, + _ => () + } + } + + /// Set the destination field for the packet + pub fn set_destination(&mut self, ip: IpAddr) { + match ip { + Ipv4Addr(a, b, c, d) => { + self.packet[self.offset + 16] = a; + self.packet[self.offset + 17] = b; + self.packet[self.offset + 18] = c; + self.packet[self.offset + 19] = d; + }, + _ => () + } + } + + /// Calculate the checksum of the packet and then set the field to the value + /// calculated + pub fn checksum(&mut self) { + let checksum = self.calculate_checksum(); + self.set_checksum(checksum); + } +} + +#[test] +fn ipv4_header_test() { + let mut packet = [0u8, ..20]; + { + let mut ipHeader = MutableIpv4Header::new(packet.as_mut_slice(), 0); + ipHeader.set_version(4); + assert_eq!(ipHeader.get_version(), 4); + + ipHeader.set_header_length(5); + assert_eq!(ipHeader.get_header_length(), 5); + + ipHeader.set_dscp(4); + assert_eq!(ipHeader.get_dscp(), 4); + + ipHeader.set_ecn(1); + assert_eq!(ipHeader.get_ecn(), 1); + + ipHeader.set_total_length(115); + assert_eq!(ipHeader.get_total_length(), 115); + + ipHeader.set_identification(257); + assert_eq!(ipHeader.get_identification(), 257); + + ipHeader.set_flags(2); + assert_eq!(ipHeader.get_flags(), 2); + + ipHeader.set_fragment_offset(257); + assert_eq!(ipHeader.get_fragment_offset(), 257); + + ipHeader.set_ttl(64); + assert_eq!(ipHeader.get_ttl(), 64); + + ipHeader.set_next_level_protocol(IpNextHeaderProtocols::Udp); + assert_eq!(ipHeader.get_next_level_protocol(), IpNextHeaderProtocols::Udp); + + ipHeader.set_source(Ipv4Addr(192, 168, 0, 1)); + assert_eq!(ipHeader.get_source(), Ipv4Addr(192, 168, 0, 1)); + + ipHeader.set_destination(Ipv4Addr(192, 168, 0, 199)); + assert_eq!(ipHeader.get_destination(), Ipv4Addr(192, 168, 0, 199)); + + ipHeader.checksum(); + assert_eq!(ipHeader.get_checksum(), 0xb64e); + } + + let refPacket = [0x45, /* ver/ihl */ + 0x11, /* dscp/ecn */ + 0x00, 0x73, /* total len */ + 0x01, 0x01, /* identification */ + 0x41, 0x01, /* flags/frag offset */ + 0x40, /* ttl */ + 0x11, /* proto */ + 0xb6, 0x4e, /* checksum */ + 0xc0, 0xa8, 0x00, 0x01, /* source ip */ + 0xc0, 0xa8, 0x00, 0xc7 /* dest ip */]; + assert_eq!(refPacket.as_slice(), packet.as_slice()); +} + +/// Structure representing an IPv6 header +pub struct Ipv6Header<'p> { + priv packet: &'p [u8], + priv offset: uint +} + +/// Structure representing a mutable IPv6 header +pub struct MutableIpv6Header<'p> { + priv packet: &'p mut [u8], + priv offset: uint +} + +impl<'p> Packet for Ipv6Header<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +impl<'p> Packet for MutableIpv6Header<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet.as_slice() } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +/// Trait implemented by anything which provides an interface to read IPv6 +/// packets +pub trait Ipv6Packet : Packet { + /// Get the version field for the packet. Should usually be 6. + fn get_version(&self) -> u8 { + self.packet()[self.offset()] >> 4 + } + + /// Get the traffic class field for the packet + fn get_traffic_class(&self) -> u8 { + let tc1 = (self.packet()[self.offset() + 0] & 0x0F) << 4; + let tc2 = self.packet()[self.offset() + 1] >> 4; + tc1 | tc2 + } + + /// Get the flow label field for the packet + fn get_flow_label(&self) -> u32 { + let fl1 = (self.packet()[self.offset() + 1] as u32 & 0xF) << 16; + let fl2 = self.packet()[self.offset() + 2] as u32 << 8; + let fl3 = self.packet()[self.offset() + 3] as u32; + fl1 | fl2 | fl3 + } + + /// Get the payload length field for the packet + fn get_payload_length(&self) -> u16 { + let len1 = self.packet()[self.offset() + 4] as u16 << 8; + let len2 = self.packet()[self.offset() + 5] as u16; + len1 | len2 + } + + /// Get the next header field for the packet + fn get_next_header(&self) -> IpNextHeaderProtocol { + self.packet()[self.offset() + 6] + } + + /// Get the hop limit field for the packet + fn get_hop_limit(&self) -> u8 { + self.packet()[self.offset() + 7] + } + + /// Get the source IP address for the packet + fn get_source(&self) -> IpAddr { + let packet = self.packet(); + let offset = self.offset(); + + let a = (packet[offset + 8] as u16 << 8) | packet[offset + 9] as u16; + let b = (packet[offset + 10] as u16 << 8) | packet[offset + 11] as u16; + let c = (packet[offset + 12] as u16 << 8) | packet[offset + 13] as u16; + let d = (packet[offset + 14] as u16 << 8) | packet[offset + 15] as u16; + let e = (packet[offset + 16] as u16 << 8) | packet[offset + 17] as u16; + let f = (packet[offset + 18] as u16 << 8) | packet[offset + 19] as u16; + let g = (packet[offset + 20] as u16 << 8) | packet[offset + 21] as u16; + let h = (packet[offset + 22] as u16 << 8) | packet[offset + 23] as u16; + + Ipv6Addr(a, b, c, d, e, f, g, h) + } + + /// Get the destination IP address for the packet + fn get_destination(&self) -> IpAddr { + let packet = self.packet(); + let offset = self.offset(); + + let a = (packet[offset + 24] as u16 << 8) | packet[offset + 25] as u16; + let b = (packet[offset + 26] as u16 << 8) | packet[offset + 27] as u16; + let c = (packet[offset + 28] as u16 << 8) | packet[offset + 29] as u16; + let d = (packet[offset + 30] as u16 << 8) | packet[offset + 31] as u16; + let e = (packet[offset + 32] as u16 << 8) | packet[offset + 33] as u16; + let f = (packet[offset + 34] as u16 << 8) | packet[offset + 35] as u16; + let g = (packet[offset + 36] as u16 << 8) | packet[offset + 37] as u16; + let h = (packet[offset + 38] as u16 << 8) | packet[offset + 39] as u16; + + Ipv6Addr(a, b, c, d, e, f, g, h) + } +} + +impl<'p> Ipv6Packet for Ipv6Header<'p> {} +impl<'p> Ipv6Packet for MutableIpv6Header<'p> {} + +impl<'p> Ipv6Header<'p> { + /// Construct a new IPv6 header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p [u8], offset: uint) -> Ipv6Header<'p> { + Ipv6Header { packet: packet, offset: offset } + } +} + +impl<'p> MutableIpv6Header<'p> { + /// Construct a new mutable IPv6 header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p mut [u8], offset: uint) -> MutableIpv6Header<'p> { + MutableIpv6Header { packet: packet, offset: offset } + } + + /// Set the version field for the packet + pub fn set_version(&mut self, version: u8) { + let ver = version << 4; + self.packet[self.offset] = (self.packet[self.offset] & 0x0F) | ver; + } + + /// Set the traffic class field for the packet + pub fn set_traffic_class(&mut self, tc: u8) { + self.packet[self.offset + 0] = (self.packet[self.offset] & 0xF0) | (tc >> 4); + self.packet[self.offset + 1] = ((tc & 0x0F) << 4) | + ((self.packet[self.offset + 1] & 0xF0) >> 4); + } + + /// Set the flow label field for the packet + pub fn set_flow_label(&mut self, label: u32) { + let lbl = ((label & 0xF0000) >> 16) as u8; + self.packet[self.offset + 1] = (self.packet[self.offset + 1] & 0xF0) | lbl; + self.packet[self.offset + 2] = ((label & 0xFF00) >> 8) as u8; + self.packet[self.offset + 3] = (label & 0x00FF) as u8; + } + + /// Set the payload length field for the packet + pub fn set_payload_length(&mut self, len: u16) { + self.packet[self.offset + 4] = (len >> 8) as u8; + self.packet[self.offset + 5] = (len & 0xFF) as u8; + } + + /// Set the next header field for the packet + pub fn set_next_header(&mut self, protocol: IpNextHeaderProtocol) { + self.packet[self.offset + 6] = protocol; + } + + /// Set the hop limit field for the packet + pub fn set_hop_limit(&mut self, limit: u8) { + self.packet[self.offset + 7] = limit; + } + + /// Set the source IP address for the packet + pub fn set_source(&mut self, ip: IpAddr) { + match ip { + Ipv6Addr(a, b, c, d, e, f, g, h) => { + self.packet[self.offset + 8] = (a >> 8) as u8; + self.packet[self.offset + 9] = (a & 0xFF) as u8; + self.packet[self.offset + 10] = (b >> 8) as u8; + self.packet[self.offset + 11] = (b & 0xFF) as u8;; + self.packet[self.offset + 12] = (c >> 8) as u8; + self.packet[self.offset + 13] = (c & 0xFF) as u8;; + self.packet[self.offset + 14] = (d >> 8) as u8; + self.packet[self.offset + 15] = (d & 0xFF) as u8;; + self.packet[self.offset + 16] = (e >> 8) as u8; + self.packet[self.offset + 17] = (e & 0xFF) as u8;; + self.packet[self.offset + 18] = (f >> 8) as u8; + self.packet[self.offset + 19] = (f & 0xFF) as u8;; + self.packet[self.offset + 20] = (g >> 8) as u8; + self.packet[self.offset + 21] = (g & 0xFF) as u8;; + self.packet[self.offset + 22] = (h >> 8) as u8; + self.packet[self.offset + 23] = (h & 0xFF) as u8; + }, + _ => () + } + } + + /// Set the destination IP address for the packet + pub fn set_destination(&mut self, ip: IpAddr) { + match ip { + Ipv6Addr(a, b, c, d, e, f, g, h) => { + self.packet[self.offset + 24] = (a >> 8) as u8; + self.packet[self.offset + 25] = (a & 0xFF) as u8; + self.packet[self.offset + 26] = (b >> 8) as u8; + self.packet[self.offset + 27] = (b & 0xFF) as u8;; + self.packet[self.offset + 28] = (c >> 8) as u8; + self.packet[self.offset + 29] = (c & 0xFF) as u8;; + self.packet[self.offset + 30] = (d >> 8) as u8; + self.packet[self.offset + 31] = (d & 0xFF) as u8;; + self.packet[self.offset + 32] = (e >> 8) as u8; + self.packet[self.offset + 33] = (e & 0xFF) as u8;; + self.packet[self.offset + 34] = (f >> 8) as u8; + self.packet[self.offset + 35] = (f & 0xFF) as u8;; + self.packet[self.offset + 36] = (g >> 8) as u8; + self.packet[self.offset + 37] = (g & 0xFF) as u8;; + self.packet[self.offset + 38] = (h >> 8) as u8; + self.packet[self.offset + 39] = (h & 0xFF) as u8; + }, + _ => () + } + } +} + +#[test] +fn ipv6_header_test() { + let mut packet = [0u8, ..40]; + { + let mut ipHeader = MutableIpv6Header::new(packet.as_mut_slice(), 0); + ipHeader.set_version(6); + assert_eq!(ipHeader.get_version(), 6); + + ipHeader.set_traffic_class(17); + assert_eq!(ipHeader.get_traffic_class(), 17); + + ipHeader.set_flow_label(0x10101); + assert_eq!(ipHeader.get_flow_label(), 0x10101); + + ipHeader.set_payload_length(0x0101); + assert_eq!(ipHeader.get_payload_length(), 0x0101); + + ipHeader.set_next_header(IpNextHeaderProtocols::Udp); + assert_eq!(ipHeader.get_next_header(), IpNextHeaderProtocols::Udp); + + ipHeader.set_hop_limit(1); + assert_eq!(ipHeader.get_hop_limit(), 1) + + let source = Ipv6Addr(0x110, 0x1001, 0x110, 0x1001, 0x110, 0x1001, 0x110, 0x1001); + ipHeader.set_source(source); + assert_eq!(ipHeader.get_source(), source); + + let dest = Ipv6Addr(0x110, 0x1001, 0x110, 0x1001, 0x110, 0x1001, 0x110, 0x1001); + ipHeader.set_destination(dest); + assert_eq!(ipHeader.get_destination(), dest); + } + + let refPacket = [0x61, /* ver/traffic class */ + 0x11, /* traffic class/flow label */ + 0x01, 0x01, /* flow label */ + 0x01, 0x01, /* payload length */ + 0x11, /* next header */ + 0x01, /* hop limit */ + 0x01, 0x10, /* source ip */ + 0x10, 0x01, + 0x01, 0x10, + 0x10, 0x01, + 0x01, 0x10, + 0x10, 0x01, + 0x01, 0x10, + 0x10, 0x01, + 0x01, 0x10, /* dest ip */ + 0x10, 0x01, + 0x01, 0x10, + 0x10, 0x01, + 0x01, 0x10, + 0x10, 0x01, + 0x01, 0x10, + 0x10, 0x01]; + assert_eq!(refPacket.as_slice(), packet.as_slice()); +} + +/// Structure representing a UDP header +pub struct UdpHeader<'p> { + priv packet: &'p [u8], + priv offset: uint +} + +/// Structure representing a mutable UDP header +pub struct MutableUdpHeader<'p> { + priv packet: &'p mut [u8], + priv offset: uint +} + +impl<'p> Packet for UdpHeader<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +impl<'p> Packet for MutableUdpHeader<'p> { + #[inline(always)] + fn packet<'p>(&'p self) -> &'p [u8] { self.packet.as_slice() } + #[inline(always)] + fn offset(&self) -> uint { self.offset } +} + +/// Trait implemented by anything which provides an interface to read UDP +/// packets +pub trait UdpPacket : Packet { + /// Get the source port for the packet + fn get_source(&self) -> u16 { + let s1 = self.packet()[self.offset() + 0] as u16 << 8; + let s2 = self.packet()[self.offset() + 1] as u16; + s1 | s2 + } + + /// Get the destination port for the packet + fn get_destination(&self) -> u16 { + let d1 = self.packet()[self.offset() + 2] as u16 << 8; + let d2 = self.packet()[self.offset() + 3] as u16; + d1 | d2 + } + + /// Get the length field for the packet + fn get_length(&self) -> u16 { + let l1 = self.packet()[self.offset() + 4] as u16 << 8; + let l2 = self.packet()[self.offset() + 5] as u16; + l1 | l2 + } + + /// Get the checksum field for the packet + fn get_checksum(&self) -> u16 { + let c1 = self.packet()[self.offset() + 6] as u16 << 8; + let c2 = self.packet()[self.offset() + 7] as u16; + c1 | c2 + } + + /// Calculate the checksum for a packet built on IPv4 + fn calculate_ipv4_checksum(&self, offset: uint) -> u16 { + let mut sum = 0u32; + let mut i = offset; + + // Checksum pseudo-header + // IPv4 source + sum = sum + (self.packet()[i + 12] as u32 << 8 | self.packet()[i + 13] as u32); + sum = sum + (self.packet()[i + 14] as u32 << 8 | self.packet()[i + 15] as u32); + + // IPv4 destination + sum = sum + (self.packet()[i + 16] as u32 << 8 | self.packet()[i + 17] as u32); + sum = sum + (self.packet()[i + 18] as u32 << 8 | self.packet()[i + 19] as u32); + + // IPv4 Next level protocol + sum = sum + self.packet()[i + 9] as u32; + + // UDP Length + sum = sum + (self.packet()[self.offset() + 4] as u32 << 8 | + self.packet()[self.offset() + 5] as u32); + + // Checksum UDP header/packet + i = self.offset(); + let len = self.offset() + self.get_length() as uint; + while i < len && i + 1 < self.packet().len() { + let word = self.packet()[i] as u32 << 8 | self.packet()[i + 1] as u32; + sum = sum + word; + i = i + 2; + } + // If the length is odd, make sure to checksum the final byte + if len & 1 != 0 && len <= self.packet().len() { + sum = sum + (self.packet()[len - 1] as u32 << 8); + } + while sum >> 16 != 0 { + sum = (sum >> 16) + (sum & 0xFFFF); + } + + return !sum as u16; + } + + /// Calculate the checksum for a packet built on IPv6 + fn calculate_ipv6_checksum(&self, offset: uint) -> u16 { + let mut sum = 0u32; + let mut i = offset + 8; + let mut len = offset + 40; + + // Checksum pseudo-header + // IPv6 source and destination + while i < len { + let word = self.packet()[i] as u32 << 8 | self.packet()[i + 1] as u32; + sum = sum + word; + i = i + 2; + } + + // IPv6 Next header + sum = sum + self.packet()[offset + 6] as u32; + + // UDP Length + sum = sum + self.get_length() as u32; + + // Checksum UDP header/packet + i = self.offset(); + len = self.offset() + self.get_length() as uint; + while i < len && i + 1 < self.packet().len() { + let word = self.packet()[i] as u32 << 8 | self.packet()[i + 1] as u32; + sum = sum + word; + i = i + 2; + } + // If the length is odd, make sure to checksum the final byte + if len & 1 != 0 && len <= self.packet().len() { + sum = sum + self.packet()[len - 1] as u32 << 8; + } + + while sum >> 16 != 0 { + sum = (sum >> 16) + (sum & 0xFFFF); + } + + return !sum as u16; + } + +} + +impl<'p> UdpPacket for UdpHeader<'p> {} +impl<'p> UdpPacket for MutableUdpHeader<'p> {} + +impl<'p> UdpHeader<'p> { + /// Construct a new UDP header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p [u8], offset: uint) -> UdpHeader<'p> { + UdpHeader { packet: packet, offset: offset } + } +} + +impl<'p> MutableUdpHeader<'p> { + /// Construct a new mutable UDP header backed by the given buffer with + /// the provided offset + pub fn new(packet: &'p mut [u8], offset: uint) -> MutableUdpHeader<'p> { + MutableUdpHeader { packet: packet, offset: offset } + } + + /// Set the source port for the packet + pub fn set_source(&mut self, port: u16) { + self.packet[self.offset + 0] = (port >> 8) as u8; + self.packet[self.offset + 1] = (port & 0xFF) as u8; + } + + /// Set the destination port for the packet + pub fn set_destination(&mut self, port: u16) { + self.packet[self.offset + 2] = (port >> 8) as u8; + self.packet[self.offset + 3] = (port & 0xFF) as u8; + } + + /// Set the length field for the packet + pub fn set_length(&mut self, len: u16) { + self.packet[self.offset + 4] = (len >> 8) as u8; + self.packet[self.offset + 5] = (len & 0xFF) as u8; + } + + /// Set the checksum field for the packet + pub fn set_checksum(&mut self, checksum: u16) { + self.packet[self.offset + 6] = (checksum >> 8) as u8; + self.packet[self.offset + 7] = (checksum & 0xFF) as u8; + } + + /// Calculate a checksum for a packet backed by IPv6, then set the field + pub fn ipv4_checksum(&mut self, offset: uint) { + let checksum = self.calculate_ipv4_checksum(offset); + // RFC 768, a checksum of zero is transmitted as all ones + if checksum != 0 { + self.set_checksum(checksum); + } else { + self.set_checksum(0xFFFF); + } + } + + /// Calculate a checksum for a packet backed by IPv6 then, set the field + pub fn ipv6_checksum(&mut self, offset: uint) { + let checksum = self.calculate_ipv6_checksum(offset); + if checksum != 0 { + self.set_checksum(checksum); + } else { + self.set_checksum(0xFFFF); + } + } + +} + +#[test] +fn udp_header_ipv4_test() { + let mut packet = [0u8, ..20 + 8 + 4]; + { + let mut ipHeader = MutableIpv4Header::new(packet.as_mut_slice(), 0); + ipHeader.set_next_level_protocol(IpNextHeaderProtocols::Udp); + ipHeader.set_source(Ipv4Addr(192, 168, 0, 1)); + ipHeader.set_destination(Ipv4Addr(192, 168, 0, 199)); + } + + // Set data + packet[20 + 8 + 0] = 't' as u8; + packet[20 + 8 + 1] = 'e' as u8; + packet[20 + 8 + 2] = 's' as u8; + packet[20 + 8 + 3] = 't' as u8; + + { + let mut udpHeader = MutableUdpHeader::new(packet.as_mut_slice(), 20); + udpHeader.set_source(12345); + assert_eq!(udpHeader.get_source(), 12345); + + udpHeader.set_destination(54321); + assert_eq!(udpHeader.get_destination(), 54321); + + udpHeader.set_length(8 + 4); + assert_eq!(udpHeader.get_length(), 8 + 4); + + udpHeader.ipv4_checksum(0); + assert_eq!(udpHeader.get_checksum(), 0x9178); + } + + let refPacket = [0x30, 0x39, /* source */ + 0xd4, 0x31, /* destination */ + 0x00, 0x0c, /* length */ + 0x91, 0x78 /* checksum*/]; + assert_eq!(refPacket.as_slice(), packet.slice(20, 28)); +} + + +#[test] +fn udp_header_ipv6_test() { + let mut packet = [0u8, ..40 + 8 + 4]; + { + let mut ipHeader = MutableIpv6Header::new(packet.as_mut_slice(), 0); + ipHeader.set_next_header(IpNextHeaderProtocols::Udp); + ipHeader.set_source(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)); + ipHeader.set_destination(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)); + } + + // Set data + packet[40 + 8 + 0] = 't' as u8; + packet[40 + 8 + 1] = 'e' as u8; + packet[40 + 8 + 2] = 's' as u8; + packet[40 + 8 + 3] = 't' as u8; + + { + let mut udpHeader = MutableUdpHeader::new(packet.as_mut_slice(), 20); + udpHeader.set_source(12345); + assert_eq!(udpHeader.get_source(), 12345); + + udpHeader.set_destination(54321); + assert_eq!(udpHeader.get_destination(), 54321); + + udpHeader.set_length(8 + 4); + assert_eq!(udpHeader.get_length(), 8 + 4); + + udpHeader.ipv6_checksum(0); + assert_eq!(udpHeader.get_checksum(), 0xf6f3); + } + + let refPacket = [0x30, 0x39, /* source */ + 0xd4, 0x31, /* destination */ + 0x00, 0x0c, /* length */ + 0xf6, 0xf3 /* checksum*/]; + assert_eq!(refPacket.as_slice(), packet.slice(20, 28)); +} + + +/// Represents a low-level protocol +pub enum Protocol { + /// A data-link layer protocol such as ethernet + DataLinkProtocol(DataLinkProto), + /// A network protocol such as IPv4 + NetworkProtocol(NetworkProto), + /// A transport protocol such as UDP + TransportProtocol(TransportProto) +} + +/// Represents a data-link layer protocol +pub enum DataLinkProto { + /// Represents a plain Ethernet protocol + EthernetProtocol, + /// Represents an Ethernet protocol with some fields filled automatically + CookedEthernetProtocol(EtherType) +} + +/// Represents a network level protocol +pub enum NetworkProto { + /// An IPv4 network protocol + Ipv4NetworkProtocol, + /// An IPv6 network protocol + Ipv6NetworkProtocol +} + +/// Represents a transport layer protocol +pub enum TransportProto { + /// Represents a transport protocol built on top of IPv4 + Ipv4TransportProtocol(IpNextHeaderProtocol), + /// Represents a transport protocol built on top of IPv6 + Ipv6TransportProtocol(IpNextHeaderProtocol) +} + +/// EtherTypes defined at: +/// http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml +/// These values should be used in the Ethernet EtherType field +/// +/// A handful of these have been selected since most are archaic and unused. +pub mod EtherTypes { + pub static Ipv4: u16 = 0x0800; + pub static Arp: u16 = 0x0806; + pub static WakeOnLan: u16 = 0x0842; + pub static Rarp: u16 = 0x8035; + pub static Ipv6: u16 = 0x86DD; +} + +pub type EtherType = u16; + +/// Protocol numbers as defined at: +/// http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml +/// Above protocol numbers last updated: 2014-01-16 +/// These values should be used in either the IPv4 Next Level Protocol field +/// or the IPv6 Next Header field. +pub mod IpNextHeaderProtocols { + pub static Hopopt: u8 = 0; // IPv6 Hop-by-Hop Option [RFC2460] + pub static Icmp: u8 = 1; // Internet Control Message [RFC792] + pub static Igmp: u8 = 2; // Internet Group Management [RFC1112] + pub static Ggp: u8 = 3; // Gateway-to-Gateway [RFC823] + pub static Ipv4: u8 = 4; // IPv4 encapsulation [RFC2003] + pub static St: u8 = 5; // Stream [RFC1190][RFC1819] + pub static Tcp: u8 = 6; // Transmission Control [RFC793] + pub static Cbt: u8 = 7; // CBT + pub static Egp: u8 = 8; // Exterior Gateway Protocol [RFC888] + pub static Igp: u8 = 9; // any private interior gateway (used by Cisco for + // their IGRP) + pub static BbnRccMon: u8 = 10; // BBN RCC Monitoring + pub static NvpII: u8 = 11; // Network Voice Protocol [RFC741] + pub static Pup: u8 = 12; // PUP + pub static Argus: u8 = 13; // ARGUS + pub static Emcon: u8 = 14; // EMCON + pub static Xnet: u8 = 15; // Cross Net Debugger + pub static Chaos: u8 = 16; // Chaos + pub static Udp: u8 = 17; // User Datagram [RFC768] + pub static Mux: u8 = 18; // Multiplexing + pub static DcnMeas: u8 = 19; // DCN Measurement Subsystems + pub static Hmp: u8 = 20; // Host Monitoring [RFC869] + pub static Prm: u8 = 21; // Packet Radio Measurement + pub static XnsIdp: u8 = 22; // XEROX NS IDP + pub static Trunk1: u8 = 23; // Trunk-1 + pub static Trunk2: u8 = 24; // Trunk-2 + pub static Leaf1: u8 = 25; // Leaf-1 + pub static Leaf2: u8 = 26; // Leaf-2 + pub static Rdp: u8 = 27; // Reliable Data Protocol [RFC908] + pub static Irtp: u8 = 28; // Internet Reliable Transaction [RFC938] + pub static IsoTp4: u8 = 29; // ISO Transport Protocol Class 4 [RFC905] + pub static Netblt: u8 = 30; // Bulk Data Transfer Protocol [RFC969] + pub static MfeNsp: u8 = 31; // MFE Network Services Protocol + pub static MeritInp: u8 = 32; // MERIT Internodal Protocol + pub static Dccp: u8 = 33; // Datagram Congestion Control Protocol [RFC4340] + pub static ThreePc: u8 = 34; // Third Party Connect Protocol + pub static Idpr: u8 = 35; // Inter-Domain Policy Routing Protocol + pub static Xtp: u8 = 36; // XTP + pub static Ddp: u8 = 37; // Datagram Delivery Protocol + pub static IdprCmtp: u8 = 38; // IDPR Control Message Transport Proto + pub static TpPlusPlus: u8 = 39; // TP++ Transport Protocol + pub static Il: u8 = 40; // IL Transport Protocol + pub static Ipv6: u8 = 41; // IPv6 encapsulation [RFC2473] + pub static Sdrp: u8 = 42; // Source Demand Routing Protocol + pub static Ipv6Route: u8 = 43; // Routing Header for IPv6 + pub static Ipv6Frag: u8 = 44; // Fragment Header for IPv6 + pub static Idrp: u8 = 45; // Inter-Domain Routing Protocol + pub static Rsvp: u8 = 46; // Reservation Protocol [RFC2205][RFC3209] + pub static Gre: u8 = 47; // Generic Routing Encapsulation [RFC1701] + pub static Dsr: u8 = 48; // Dynamic Source Routing Protocol [RFC4728] + pub static Bna: u8 = 49; // BNA + pub static Esp: u8 = 50; // Encap Security Payload [RFC4303] + pub static Ah: u8 = 51; // Authentication Header [RFC4302] + pub static INlsp: u8 = 52; // Integrated Net Layer Security TUBA + pub static Swipe: u8 = 53; // IP with Encryption + pub static Narp: u8 = 54; // NBMA Address Resolution Protocol [RFC1735] + pub static Mobile: u8 = 55; // IP Mobility + pub static Tlsp: u8 = 56; // Transport Layer Security Protocol using Kryptonet key + // management + pub static Skip: u8 = 57; // SKIP + pub static Ipv6Icmp: u8 = 58; // ICMP for IPv6 [RFC2460] + pub static Ipv6NoNxt: u8 = 59; // No Next Header for IPv6 [RFC2460] + pub static Ipv6Opts: u8 = 60; // Destination Options for IPv6 [RFC2460] + pub static HostInternal: u8 = 61; // any host internal protocol + pub static Cftp: u8 = 62; // CFTP + pub static LocalNetwork: u8 = 63; // any local network + pub static SatExpak: u8 = 64; // SATNET and Backroom EXPAK + pub static Kryptolan: u8 = 65; // Kryptolan + pub static Rvd: u8 = 66; // MIT Remote Virtual Disk Protocol + pub static Ippc: u8 = 67; // Internet Pluribus Packet Core + pub static DistributedFs: u8 = 68; // any distributed file system + pub static SatMon: u8 = 69; // SATNET Monitoring + pub static Visa: u8 = 70; // VISA Protocol + pub static Ipcv: u8 = 71; // Internet Packet Core Utility + pub static Cpnx: u8 = 72; // Computer Protocol Network Executive + pub static Cphb: u8 = 73; // Computer Protocol Heart Beat + pub static Wsn: u8 = 74; // Wang Span Network + pub static Pvp: u8 = 75; // Packet Video Protocol + pub static BrSatMon: u8 = 76; // Backroom SATNET Monitoring + pub static SunNd: u8 = 77; // SUN ND PROTOCOL-Temporary + pub static WbMon: u8 = 78; // WIDEBAND Monitoring + pub static WbExpak: u8 = 79; // WIDEBAND EXPAK + pub static IsoIp: u8 = 80; // ISO Internet Protocol + pub static Vmtp: u8 = 81; // VMTP + pub static SecureVmtp: u8 = 82; // SECURE-VMTP + pub static Vines: u8 = 83; // VINES + pub static TtpOrIptm: u8 = 84; // Transaction Transport Protocol/IP Traffic Manager + pub static NsfnetIgp: u8 = 85; // NSFNET-IGP + pub static Dgp: u8 = 86; // Dissimilar Gateway Protocol + pub static Tcf: u8 = 87; // TCF + pub static Eigrp: u8 = 88; // EIGRP + pub static OspfigP: u8 = 89; // OSPFIGP [RFC1583][RFC2328][RFC5340] + pub static SpriteRpc: u8 = 90; // Sprite RPC Protocol + pub static Larp: u8 = 91; // Locus Address Resolution Protocol + pub static Mtp: u8 = 92; // Multicast Transport Protocol + pub static Ax25: u8 = 93; // AX.25 Frames + pub static IpIp: u8 = 94; // IP-within-IP Encapsulation Protocol + pub static Micp: u8 = 95; // Mobile Internetworking Control Pro. + pub static SccSp: u8 = 96; // Semaphore Communications Sec. Pro. + pub static Etherip: u8 = 97; // Ethernet-within-IP Encapsulation [RFC3378] + pub static Encap: u8 = 98; // Encapsulation Header [RFC1241] + pub static PrivEncryption: u8 = 99; // any private encryption scheme + pub static Gmtp: u8 = 100; // GMTP + pub static Ifmp: u8 = 101; // Ipsilon Flow Management Protocol + pub static Pnni: u8 = 102; // PNNI over IP + pub static Pim: u8 = 103; // Protocol Independent Multicast [RFC4601] + pub static Aris: u8 = 104; // ARIS + pub static Scps: u8 = 105; // SCPS + pub static Qnx: u8 = 106; // QNX + pub static AN: u8 = 107; // Active Networks + pub static IpComp: u8 = 108; // IP Payload Compression Protocol [RFC2393] + pub static Snp: u8 = 109; // Sitara Networks Protocol + pub static CompaqPeer: u8 = 110; // Compaq Peer Protocol + pub static IpxInIp: u8 = 111; // IPX in IP + pub static Vrrp: u8 = 112; // Virtual Router Redundancy Protocol [RFC5798] + pub static Pgm: u8 = 113; // PGM Reliable Transport Protocol + pub static ZeroHop: u8 = 114; // any 0-hop protocol + pub static L2tp: u8 = 115; // Layer Two Tunneling Protocol [RFC3931] + pub static Ddx: u8 = 116; // D-II Data Exchange (DDX) + pub static Iatp: u8 = 117; // Interactive Agent Transfer Protocol + pub static Stp: u8 = 118; // Schedule Transfer Protocol + pub static Srp: u8 = 119; // SpectraLink Radio Protocol + pub static Uti: u8 = 120; // UTI + pub static Smp: u8 = 121; // Simple Message Protocol + pub static Sm: u8 = 122; // Simple Multicast Protocol + pub static Ptp: u8 = 123; // Performance Transparency Protocol + pub static IsisOverIpv4: u8 = 124; // + pub static Fire: u8 = 125; // + pub static Crtp: u8 = 126; // Combat Radio Transport Protocol + pub static Crudp: u8 = 127; // Combat Radio User Datagram + pub static Sscopmce: u8 = 128; // + pub static Iplt: u8 = 129; // + pub static Sps: u8 = 130; // Secure Packet Shield + pub static Pipe: u8 = 131; // Private IP Encapsulation within IP + pub static Sctp: u8 = 132; // Stream Control Transmission Protocol + pub static Fc: u8 = 133; // Fibre Channel [RFC6172] + pub static RsvpE2eIgnore: u8 = 134; // [RFC3175] + pub static MobilityHeader: u8 = 135; // [RFC6275] + pub static UdpLite: u8 = 136; // [RFC3828] + pub static MplsInIp: u8 = 137; // [RFC4023] + pub static Manet: u8 = 138; // MANET Protocols [RFC5498] + pub static Hip: u8 = 139; // Host Identity Protocol [RFC5201] + pub static Shim6: u8 = 140; // Shim6 Protocol [RFC5533] + pub static Wesp: u8 = 141; // Wrapped Encapsulating Security Payload [RFC5840] + pub static Rohc: u8 = 142; // Robust Header Compression [RFC5858] + pub static Test1: u8 = 253; // Use for experimentation and testing [RFC3692] + pub static Test2: u8 = 254; // Use for experimentation and testing [RFC3692] + pub static Reserved: u8 = 255; // +} + +pub type IpNextHeaderProtocol = u8; + +#[cfg(test)] +pub mod test { + use realstd::clone::Clone; + use realstd::result::{Ok, Err}; + use realstd::container::Container; + use realstd::option::{Some}; + use realstd::str::StrSlice; + use realstd::io::net::raw::*; + use realstd::io::net::raw::{Ipv4Packet, UdpPacket, IpNextHeaderProtocols}; + use realstd::task::spawn; + use realstd::io::net::ip::{IpAddr, Ipv4Addr, Ipv6Addr}; + use realstd::vec::ImmutableVector; + use realstd::iter::Iterator; + use realstd::vec::{Vector}; + + use netsupport; + + // This needs to be redefined here otherwise imports do not work correctly + macro_rules! iotest ( + { fn $name:ident() $b:block $($a:attr)* } => ( + mod $name { + #[allow(unused_imports)]; + + use realstd::io; + use realstd::prelude::*; + use realstd::io::*; + use realstd::io::fs::*; + use realstd::io::test::*; + use realstd::io::net::tcp::*; + use realstd::io::net::ip::*; + use realstd::io::net::udp::*; + use realstd::io::net::raw::*; + use std::io::net::raw::test::*; + use std::io::net::raw::EtherTypes; + #[cfg(unix)] + use realstd::io::net::unix::*; + use realstd::io::timer::*; + use realstd::io::process::*; + use realstd::unstable::running_on_valgrind; + use realstd::str; + use realstd::util; + + fn f() $b + + $($a)* #[test] fn green() { f() } + $($a)* #[test] fn native() { + use native; + let (tx, rx) = channel(); + native::task::spawn(proc() { tx.send(f()) }); + rx.recv(); + } + } + ) + ) + + pub static ETHERNET_HEADER_LEN: u16 = 14; + pub static IPV4_HEADER_LEN: u16 = 20; + pub static IPV6_HEADER_LEN: u16 = 40; + pub static UDP_HEADER_LEN: u16 = 8; + pub static TEST_DATA_LEN: u16 = 4; + + pub fn layer4_test(ip: IpAddr, headerLen: uint) { + let message = "message"; + + spawn( proc() { + let mut buf: ~[u8] = ~[0, ..128]; + match RawSocket::new(get_proto(ip)) { + Ok(mut sock) => match sock.recvfrom(buf) { + Ok((len, Some(~IpAddress(addr)))) => { + assert_eq!(buf.slice(headerLen, message.len()), message.as_bytes()); + assert_eq!(len, message.len()); + assert!(addr == ip, "addr != ip"); + }, + _ => fail!() + }, + Err(_) => fail!() + } + }); + + match RawSocket::new(get_proto(ip)) { + Ok(mut sock) => match sock.sendto(message.as_bytes(), ~IpAddress(ip)) { + Ok(res) => assert_eq!(res as uint, message.len()), + Err(_) => fail!() + }, + Err(_) => fail!() + } + + fn get_proto(ip: IpAddr) -> Protocol { + match ip { + Ipv4Addr(..) => + TransportProtocol(Ipv4TransportProtocol(IpNextHeaderProtocols::Test1)), + Ipv6Addr(..) => + TransportProtocol(Ipv6TransportProtocol(IpNextHeaderProtocols::Test1)) + } + } + } + + iotest!(fn layer4_ipv4() { + layer4_test(Ipv4Addr(127, 0, 0, 1), IPV4_HEADER_LEN as uint); + } #[cfg(hasroot)]) + + iotest!(fn layer4_ipv6() { + layer4_test(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), IPV6_HEADER_LEN as uint); + } #[cfg(hasroot)]) + + pub fn build_ipv4_header(packet: &mut [u8], offset: uint) { + let mut ipHeader = MutableIpv4Header::new(packet, offset); + + ipHeader.set_version(4); + ipHeader.set_header_length(5); + ipHeader.set_total_length(IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN); + ipHeader.set_ttl(4); + ipHeader.set_next_level_protocol(IpNextHeaderProtocols::Udp); + ipHeader.set_source(Ipv4Addr(127, 0, 0, 1)); + ipHeader.set_destination(Ipv4Addr(127, 0, 0, 1)); + ipHeader.checksum(); + } + + pub fn build_ipv6_header(packet: &mut [u8], offset: uint) { + let mut ipHeader = MutableIpv6Header::new(packet, offset); + + ipHeader.set_version(6); + ipHeader.set_payload_length(UDP_HEADER_LEN + TEST_DATA_LEN); + ipHeader.set_next_header(IpNextHeaderProtocols::Udp); + ipHeader.set_hop_limit(4); + ipHeader.set_source(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)); + ipHeader.set_destination(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)); + } + + pub fn build_udp_header(packet: &mut [u8], offset: uint) { + let mut udpHeader = MutableUdpHeader::new(packet, offset); + + udpHeader.set_source(1234); // Arbitary port number + udpHeader.set_destination(1234); + udpHeader.set_length(UDP_HEADER_LEN + TEST_DATA_LEN); + } + + pub fn build_udp4_packet(packet: &mut [u8], start: uint) { + build_ipv4_header(packet, start); + build_udp_header(packet, IPV4_HEADER_LEN as uint); + + let dataStart = IPV4_HEADER_LEN + UDP_HEADER_LEN; + packet[dataStart + 0] = 't' as u8; + packet[dataStart + 1] = 'e' as u8; + packet[dataStart + 2] = 's' as u8; + packet[dataStart + 3] = 't' as u8; + + MutableUdpHeader::new(packet, IPV4_HEADER_LEN as uint).ipv4_checksum(start); + } + + pub fn build_udp6_packet(packet: &mut [u8], start: uint) { + build_ipv6_header(packet, start); + build_udp_header(packet, IPV6_HEADER_LEN as uint); + + let dataStart = IPV6_HEADER_LEN + UDP_HEADER_LEN; + packet[dataStart + 0] = 't' as u8; + packet[dataStart + 1] = 'e' as u8; + packet[dataStart + 2] = 's' as u8; + packet[dataStart + 3] = 't' as u8; + + MutableUdpHeader::new(packet, IPV6_HEADER_LEN as uint).ipv6_checksum(start); + } + + pub fn get_test_interface() -> NetworkInterface { + (**netsupport::get_network_interfaces() + .as_slice().iter() + .filter(|x| x.is_loopback()) + .next() + .unwrap()) + .clone() + } + + pub fn same_ports(packet1: &[u8], packet2: &[u8], offset: uint) -> bool { + { + let ip1 = Ipv4Header::new(packet1, offset); + let ip2 = Ipv4Header::new(packet2, offset); + + // Check we have an IPv4/UDP packet + if ip1.get_version() != 4 || ip2.get_version() != 4 || + ip1.get_next_level_protocol() != IpNextHeaderProtocols::Udp || + ip2.get_next_level_protocol() != IpNextHeaderProtocols::Udp { + return false; + } + } + + let udp1 = UdpHeader::new(packet1, offset + IPV4_HEADER_LEN as uint); + let udp2 = UdpHeader::new(packet2, offset + IPV4_HEADER_LEN as uint); + + if udp1.get_source() == udp2.get_source() && + udp1.get_destination() == udp2.get_destination() { + return true; + } + + return false; + } + + iotest!(fn layer3_ipv4_test() { + let sendAddr = Ipv4Addr(127, 0, 0, 1); + let mut packet = [0u8, ..IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN]; + build_udp4_packet(packet.as_mut_slice(), 0); + + spawn( proc() { + let mut buf: ~[u8] = ~[0, ..128]; + match RawSocket::new(NetworkProtocol(Ipv4NetworkProtocol)) { + Ok(mut sock) => match sock.recvfrom(buf) { + Ok((len, Some(~IpAddress(addr)))) => { + assert_eq!(buf.slice(0, packet.len()), packet.as_slice()); + assert_eq!(len, packet.len()); + assert!(addr == sendAddr, "addr != sendAddr"); + }, + _ => fail!() + }, + Err(_) => fail!() + } + }); + + match RawSocket::new(NetworkProtocol(Ipv4NetworkProtocol)) { + Ok(mut sock) => match sock.sendto(packet, ~IpAddress(sendAddr)) { + Ok(res) => assert_eq!(res as uint, packet.len()), + Err(_) => fail!() + }, + Err(_) => fail!() + } + + } #[cfg(hasroot)]) + + iotest!(fn layer3_ipv6_test() { + let sendAddr = Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1); + let mut packet = [0u8, ..IPV6_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN]; + build_udp6_packet(packet.as_mut_slice(), 0); + + spawn( proc() { + let mut buf: ~[u8] = ~[0, ..128]; + match RawSocket::new(NetworkProtocol(Ipv6NetworkProtocol)) { + Ok(mut sock) => match sock.recvfrom(buf) { + Ok((len, Some(~IpAddress(addr)))) => { + assert_eq!(buf.slice(0, packet.len()), packet.as_slice()); + assert_eq!(len, packet.len()); + assert!(addr == sendAddr, "addr != sendAddr"); + }, + _ => fail!() + }, + Err(_) => fail!() + } + }); + + match RawSocket::new(NetworkProtocol(Ipv6NetworkProtocol)) { + Ok(mut sock) => match sock.sendto(packet, ~IpAddress(sendAddr)) { + Ok(res) => assert_eq!(res as uint, packet.len()), + Err(_) => fail!() + }, + Err(_) => fail!() + } + + } #[cfg(hasroot)]) + + iotest!(fn layer2_cooked_test() { + let interface = get_test_interface(); + let interface2 = get_test_interface(); + + let mut packet = [0u8, ..32]; + + build_udp4_packet(packet.as_mut_slice(), 0); + + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + + spawn( proc() { + let mut buf: ~[u8] = ~[0, ..128]; + match RawSocket::new(DataLinkProtocol(CookedEthernetProtocol(EtherTypes::Ipv4))) { + Ok(mut sock) => { + tx.send(()); + loop { + match sock.recvfrom(buf) { + Ok((len, Some(~NetworkAddress(ni)))) => { + if len == packet.len() && same_ports(packet.as_slice(), buf, 0) { + assert_eq!(buf.slice(0, packet.len()), packet.as_slice()); + assert!(*ni == interface, "*ni != interface"); + break; + } + }, + _ => fail!() + } + } + }, + Err(_) => fail!() + } + rx2.recv(); + }); + + match RawSocket::new(DataLinkProtocol(CookedEthernetProtocol(EtherTypes::Ipv4))) { + Ok(mut sock) => { + rx.recv(); + match sock.sendto(packet, ~NetworkAddress(~interface2)) { + Ok(res) => assert_eq!(res as uint, packet.len()), + Err(_) => fail!() + } + }, + Err(_) => fail!() + } + tx2.send(()); + } #[cfg(hasroot)] #[ignore(cfg(not(target_os = "linux")))]) + + iotest!(fn layer2_test() { + let interface = get_test_interface(); + let interface2 = interface.clone(); + + let mut packet = [0u8, ..46]; + + { + let mut ethernetHeader = MutableEthernetHeader::new(packet.as_mut_slice(), 0); + ethernetHeader.set_source(interface.mac_address()); + ethernetHeader.set_destination(interface.mac_address()); + ethernetHeader.set_ethertype(EtherTypes::Ipv4); + } + + build_udp4_packet(packet.as_mut_slice(), ETHERNET_HEADER_LEN as uint); + + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + + spawn( proc() { + let mut buf: ~[u8] = ~[0, ..128]; + match RawSocket::new(DataLinkProtocol(EthernetProtocol)) { + Ok(mut sock) => { + tx.send(()); + loop { + match sock.recvfrom(buf) { + Ok((len, Some(~NetworkAddress(ni)))) => { + if len == packet.len() && + same_ports(packet.as_slice(), buf, ETHERNET_HEADER_LEN as uint) { + assert_eq!(buf.slice(0, packet.len()), packet.as_slice()); + assert!(*ni == interface, "*ni != interface"); + break; + } + }, + _ => fail!() + } + } + }, + Err(_) => fail!() + } + rx2.recv(); + }); + + match RawSocket::new(DataLinkProtocol(EthernetProtocol)) { + Ok(mut sock) => { + rx.recv(); + match sock.sendto(packet, ~NetworkAddress(~interface2)) { + Ok(res) => assert_eq!(res as uint, packet.len()), + Err(_) => fail!() + } + }, + Err(_) => fail!() + } + tx2.send(()); + } #[cfg(hasroot)] #[ignore(cfg(not(target_os = "linux")))]) + +} diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 7b1dd114d348f..4f13530c3e42c 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -183,7 +183,7 @@ mod test { Ok(..) => fail!(), Err(e) => assert_eq!(e.kind, PermissionDenied), } - } #[ignore(cfg(windows))] #[ignore(cfg(target_os = "android"))]) + } #[ignore(cfg(windows))] #[ignore(cfg(target_os = "android"))] #[ignore(cfg(hasroot))]) iotest!(fn connect_error() { let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index e262055632158..e1b39c936181a 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -102,7 +102,7 @@ mod test { Ok(..) => fail!(), Err(e) => assert_eq!(e.kind, PermissionDenied), } - } #[ignore(cfg(windows))] #[ignore(cfg(target_os = "android"))]) + } #[ignore(cfg(windows))] #[ignore(cfg(target_os = "android"))] #[ignore(cfg(hasroot))]) iotest!(fn socket_smoke_test_ip4() { let server_ip = next_test_ip4(); diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 9eeaf4635a482..92088d8ef7d25 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -33,6 +33,8 @@ macro_rules! iotest ( use io::net::tcp::*; use io::net::ip::*; use io::net::udp::*; + use io::net::raw::*; + use io::net::raw::test::*; #[cfg(unix)] use io::net::unix::*; use io::timer::*; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 17c0e2235c0bf..016267aa9be3a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -74,6 +74,7 @@ #[cfg(test)] extern crate native; #[cfg(test)] extern crate green; #[cfg(test)] #[phase(syntax, link)] extern crate log; +#[cfg(test)] extern crate netsupport; // Make and rand accessible for benchmarking/testcases #[cfg(test)] extern crate rand; @@ -213,18 +214,27 @@ pub mod rt; // can be resolved within libstd. #[doc(hidden)] mod std { + pub use cast; pub use clone; pub use cmp; pub use comm; + pub use container; pub use fmt; pub use hash; pub use io; + pub use iter; pub use kinds; pub use local_data; + pub use num; pub use option; pub use os; + pub use prelude; + pub use result; pub use rt; pub use str; + pub use task; pub use to_str; pub use unstable; + pub use vec; + pub use vec_ng; } diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 0fb7a5f85032c..fed47e7c202b3 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -111,6 +111,7 @@ pub use libc::funcs::posix01::stat_::*; pub use libc::funcs::posix01::unistd::*; pub use libc::funcs::posix01::glob::*; pub use libc::funcs::posix01::mman::*; +pub use libc::funcs::posix01::net::*; pub use libc::funcs::posix08::unistd::*; pub use libc::funcs::bsd43::*; @@ -121,6 +122,8 @@ pub use libc::funcs::extra::*; pub use libc::funcs::extra::kernel32::*; #[cfg(target_os = "win32")] pub use libc::funcs::extra::msvcrt::*; +#[cfg(target_os = "win32")] +pub use libc::funcs::extra::winsock::*; // Explicit export lists for the intersection (provided here) mean that // you can write more-platform-agnostic code if you stick to just these @@ -268,6 +271,7 @@ pub mod types { pub enum timezone {} } pub mod bsd44 { + use libc::types::common::c95::{c_void}; use libc::types::os::arch::c95::{c_char, c_int, c_uint}; pub type socklen_t = u32; @@ -324,6 +328,16 @@ pub mod types { sun_family: sa_family_t, sun_path: [c_char, ..108] } + + pub struct ifaddrs { + ifa_next: *ifaddrs, + ifa_name: *c_char, + ifa_flags: c_uint, + ifa_addr: *sockaddr, + ifa_netmask: *sockaddr, + ifa_ifu: *sockaddr, // FIXME This should be a union + ifa_data: *c_void + } } } @@ -510,7 +524,18 @@ pub mod types { } pub mod posix08 {} pub mod bsd44 {} - pub mod extra {} + pub mod extra { + use libc::types::os::arch::c95::{c_ushort, c_int, c_uchar}; + pub struct sockaddr_ll { + sll_family: c_ushort, + sll_protocol: c_ushort, + sll_ifindex: c_int, + sll_hatype: c_ushort, + sll_pkttype: c_uchar, + sll_halen: c_uchar, + sll_addr: [c_uchar, ..8] + } + } } #[cfg(target_arch = "x86_64")] @@ -595,6 +620,16 @@ pub mod types { pub mod bsd44 { } pub mod extra { + use libc::types::os::arch::c95::{c_ushort, c_int, c_uchar}; + pub struct sockaddr_ll { + sll_family: c_ushort, + sll_protocol: c_ushort, + sll_ifindex: c_int, + sll_hatype: c_ushort, + sll_pkttype: c_uchar, + sll_halen: c_uchar, + sll_addr: [c_uchar, ..8] + } } } } @@ -639,6 +674,7 @@ pub mod types { pub enum timezone {} } pub mod bsd44 { + use libc::types::common::c95::{c_void}; use libc::types::os::arch::c95::{c_char, c_int, c_uint}; pub type socklen_t = u32; @@ -701,6 +737,16 @@ pub mod types { sun_family: sa_family_t, sun_path: [c_char, ..104] } + pub struct ifaddrs { + ifa_next: *ifaddrs, + ifa_name: *c_char, + ifa_flags: c_uint, + ifa_addr: *sockaddr, + ifa_netmask: *sockaddr, + ifa_dstaddr: *sockaddr, + ifa_data: *c_void + } + } } @@ -1209,6 +1255,7 @@ pub mod types { } pub mod bsd44 { + use libc::types::common::c95::{c_void}; use libc::types::os::arch::c95::{c_char, c_int, c_uint}; pub type socklen_t = c_int; @@ -1271,6 +1318,16 @@ pub mod types { sun_family: sa_family_t, sun_path: [c_char, ..104] } + pub struct ifaddrs { + ifa_next: *ifaddrs, + ifa_name: *c_char, + ifa_flags: c_uint, + ifa_addr: *sockaddr, + ifa_netmask: *sockaddr, + ifa_dstaddr: *sockaddr, + ifa_data: *c_void + } + } } @@ -1595,6 +1652,7 @@ pub mod consts { pub static AF_INET6: c_int = 23; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; @@ -1612,12 +1670,14 @@ pub mod consts { pub static SO_BROADCAST: c_int = 32; pub static SO_REUSEADDR: c_int = 4; + pub static IFF_LOOPBACK: c_int = 4; + pub static SHUT_RD: c_int = 0; pub static SHUT_WR: c_int = 1; pub static SHUT_RDWR: c_int = 2; } pub mod extra { - use libc::types::os::arch::c95::c_int; + use libc::types::os::arch::c95::{c_int, c_long}; use libc::types::os::arch::extra::{WORD, DWORD, BOOL}; pub static TRUE : BOOL = 1; @@ -1836,6 +1896,10 @@ pub mod consts { pub static PIPE_ACCEPT_REMOTE_CLIENTS: DWORD = 0x00000000; pub static PIPE_REJECT_REMOTE_CLIENTS: DWORD = 0x00000008; pub static PIPE_UNLIMITED_INSTANCES: DWORD = 255; + + pub static IPPROTO_RAW : c_int = 255; + + pub static FIONBIO : c_long = -0x7FFB9982; } pub mod sysconf { } @@ -2291,6 +2355,12 @@ pub mod consts { pub mod posix01 { use libc::types::os::arch::c95::{c_int, size_t}; + pub static F_DUPFD : c_int = 0; + pub static F_GETFD : c_int = 1; + pub static F_SETFD : c_int = 2; + pub static F_GETFL : c_int = 3; + pub static F_SETFL : c_int = 4; + pub static SIGTRAP : c_int = 5; pub static GLOB_ERR : c_int = 1 << 0; @@ -2374,17 +2444,21 @@ pub mod consts { pub static MADV_UNMERGEABLE : c_int = 13; pub static MADV_HWPOISON : c_int = 100; + pub static IFF_LOOPBACK: c_int = 0x8; + pub static AF_UNIX: c_int = 1; pub static AF_INET: c_int = 2; pub static AF_INET6: c_int = 10; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; pub static IP_MULTICAST_TTL: c_int = 33; pub static IP_MULTICAST_LOOP: c_int = 34; pub static IP_TTL: c_int = 2; + pub static IP_HDRINCL: c_int = 2; pub static IP_ADD_MEMBERSHIP: c_int = 35; pub static IP_DROP_MEMBERSHIP: c_int = 36; pub static IPV6_ADD_MEMBERSHIP: c_int = 20; @@ -2406,8 +2480,12 @@ pub mod consts { pub mod extra { use libc::types::os::arch::c95::c_int; + pub static AF_PACKET : c_int = 17; + pub static IPPROTO_RAW : c_int = 255; + pub static O_RSYNC : c_int = 1052672; pub static O_DSYNC : c_int = 4096; + pub static O_NONBLOCK : c_int = 2048; pub static O_SYNC : c_int = 1052672; pub static PROT_GROWSDOWN : c_int = 0x010000000; @@ -2740,6 +2818,12 @@ pub mod consts { pub mod posix01 { use libc::types::os::arch::c95::{c_int, size_t}; + pub static F_DUPFD : c_int = 0; + pub static F_GETFD : c_int = 1; + pub static F_SETFD : c_int = 2; + pub static F_GETFL : c_int = 3; + pub static F_SETFL : c_int = 4; + pub static SIGTRAP : c_int = 5; pub static GLOB_APPEND : c_int = 0x0001; @@ -2832,12 +2916,14 @@ pub mod consts { pub static AF_UNIX: c_int = 1; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; pub static IP_MULTICAST_TTL: c_int = 10; pub static IP_MULTICAST_LOOP: c_int = 11; pub static IP_TTL: c_int = 4; + pub static IP_HDRINCL: c_int = 2; pub static IP_ADD_MEMBERSHIP: c_int = 12; pub static IP_DROP_MEMBERSHIP: c_int = 13; pub static IPV6_ADD_MEMBERSHIP: c_int = 12; @@ -2850,6 +2936,8 @@ pub mod consts { pub static SO_BROADCAST: c_int = 0x0020; pub static SO_REUSEADDR: c_int = 0x0004; + pub static IFF_LOOPBACK: c_int = 0x8; + pub static SHUT_RD: c_int = 0; pub static SHUT_WR: c_int = 1; pub static SHUT_RDWR: c_int = 2; @@ -2858,6 +2946,7 @@ pub mod consts { use libc::types::os::arch::c95::c_int; pub static O_SYNC : c_int = 128; + pub static O_NONBLOCK : c_int = 4; pub static CTL_KERN: c_int = 1; pub static KERN_PROC: c_int = 14; pub static KERN_PROC_PATHNAME: c_int = 12; @@ -2869,6 +2958,8 @@ pub mod consts { pub static MAP_STACK : c_int = 0x0400; pub static MAP_NOSYNC : c_int = 0x0800; pub static MAP_NOCORE : c_int = 0x020000; + + pub static IPPROTO_RAW : c_int = 255; } pub mod sysconf { use libc::types::os::arch::c95::c_int; @@ -3135,6 +3226,12 @@ pub mod consts { pub mod posix01 { use libc::types::os::arch::c95::{c_int, size_t}; + pub static F_DUPFD : c_int = 0; + pub static F_GETFD : c_int = 1; + pub static F_SETFD : c_int = 2; + pub static F_GETFL : c_int = 3; + pub static F_SETFL : c_int = 4; + pub static SIGTRAP : c_int = 5; pub static GLOB_APPEND : c_int = 0x0001; @@ -3215,12 +3312,14 @@ pub mod consts { pub static AF_INET6: c_int = 30; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; pub static IP_MULTICAST_TTL: c_int = 10; pub static IP_MULTICAST_LOOP: c_int = 11; pub static IP_TTL: c_int = 4; + pub static IP_HDRINCL: c_int = 2; pub static IP_ADD_MEMBERSHIP: c_int = 12; pub static IP_DROP_MEMBERSHIP: c_int = 13; pub static IPV6_ADD_MEMBERSHIP: c_int = 12; @@ -3233,6 +3332,8 @@ pub mod consts { pub static SO_BROADCAST: c_int = 0x0020; pub static SO_REUSEADDR: c_int = 0x0004; + pub static IFF_LOOPBACK: c_int = 0x8; + pub static SHUT_RD: c_int = 0; pub static SHUT_WR: c_int = 1; pub static SHUT_RDWR: c_int = 2; @@ -3242,6 +3343,7 @@ pub mod consts { pub static O_DSYNC : c_int = 4194304; pub static O_SYNC : c_int = 128; + pub static O_NONBLOCK : c_int = 4; pub static F_FULLFSYNC : c_int = 51; pub static MAP_COPY : c_int = 0x0002; @@ -3251,6 +3353,8 @@ pub mod consts { pub static MAP_HASSEMAPHORE : c_int = 0x0200; pub static MAP_NOCACHE : c_int = 0x0400; pub static MAP_JIT : c_int = 0x0800; + + pub static IPPROTO_RAW : c_int = 255; } pub mod sysconf { use libc::types::os::arch::c95::c_int; @@ -3643,7 +3747,7 @@ pub mod funcs { pub fn open(path: *c_char, oflag: c_int, mode: c_int) -> c_int; pub fn creat(path: *c_char, mode: mode_t) -> c_int; - pub fn fcntl(fd: c_int, cmd: c_int) -> c_int; + pub fn fcntl(fd: c_int, cmd: c_int, ...) -> c_int; } } @@ -3873,6 +3977,14 @@ pub mod funcs { -> c_int; } } + + pub mod net { + use libc::types::os::arch::c95::{c_char, c_uint}; + + extern { + pub fn if_nametoindex(ifname: *c_char) -> c_uint; + } + } } #[cfg(target_os = "win32")] @@ -3888,6 +4000,9 @@ pub mod funcs { pub mod mman { } + + pub mod net { + } } @@ -3904,7 +4019,7 @@ pub mod funcs { #[cfg(not(windows))] pub mod bsd43 { use libc::types::common::c95::{c_void}; - use libc::types::os::common::bsd44::{socklen_t, sockaddr}; + use libc::types::os::common::bsd44::{socklen_t, sockaddr, ifaddrs}; use libc::types::os::arch::c95::{c_int, size_t}; use libc::types::os::arch::posix88::ssize_t; @@ -3933,6 +4048,8 @@ pub mod funcs { pub fn sendto(socket: c_int, buf: *c_void, len: size_t, flags: c_int, addr: *sockaddr, addrlen: socklen_t) -> ssize_t; + pub fn getifaddrs(ifap: *mut *ifaddrs) -> c_int; + pub fn freeifaddrs(ifa: *ifaddrs); pub fn shutdown(socket: c_int, how: c_int) -> c_int; } } @@ -4015,6 +4132,7 @@ pub mod funcs { extern { pub fn getdtablesize() -> c_int; + pub fn ioctl(d: c_int, request: c_int, ...) -> c_int; pub fn madvise(addr: *c_void, len: size_t, advice: c_int) -> c_int; pub fn mincore(addr: *c_void, len: size_t, vec: *c_uchar) @@ -4263,5 +4381,14 @@ pub mod funcs { flags: c_int) -> c_int; } } + + pub mod winsock { + use libc::types::os::arch::c95::{c_int, c_long, c_ulong}; + use libc::types::os::common::bsd44::SOCKET; + + extern "system" { + pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *c_ulong) -> c_int; + } + } } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 60933aeb38b7a..a2c7ea5623bcc 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -24,6 +24,7 @@ use ai = io::net::addrinfo; use io; use io::{IoError, IoResult}; use io::net::ip::{IpAddr, SocketAddr}; +use io::net::raw::{NetworkAddress, Protocol}; use io::process::{ProcessConfig, ProcessExit}; use io::signal::Signum; use io::{FileMode, FileAccess, FileStat, FilePermission}; @@ -151,6 +152,7 @@ pub trait IoFactory { fn unix_connect(&mut self, path: &CString) -> Result<~RtioPipe, IoError>; fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>, hint: Option) -> Result<~[ai::Info], IoError>; + fn raw_socket_new(&mut self, protocol: Protocol) -> Result<~RtioRawSocket, IoError>; // filesystem operations fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior) -> ~RtioFileStream; @@ -232,6 +234,11 @@ pub trait RtioUdpSocket : RtioSocket { fn clone(&self) -> ~RtioUdpSocket; } +pub trait RtioRawSocket { + fn recvfrom(&mut self, buf: &mut [u8]) -> Result<(uint, Option<~NetworkAddress>), IoError>; + fn sendto(&mut self, buf: &[u8], dst: ~NetworkAddress) -> Result; +} + pub trait RtioTimer { fn sleep(&mut self, msecs: u64); fn oneshot(&mut self, msecs: u64) -> Receiver<()>;