// Copyright 2013 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. use std::cast; use std::io::net::ip; use std::io; use std::libc; use std::mem; use std::rt::rtio; use std::unstable::intrinsics; use super::IoResult; use super::file::keep_going; #[cfg(windows)] pub type sock_t = libc::SOCKET; #[cfg(unix)] pub type sock_t = super::file::fd_t; pub struct TcpStream { priv fd: sock_t, } #[cfg(target_endian = "big")] pub fn htons(x: u16) -> u16 { x } #[cfg(target_endian = "big")] pub fn ntohs(x: u16) -> u16 { x } #[cfg(target_endian = "little")] pub fn htons(u: u16) -> u16 { unsafe { intrinsics::bswap16(u as i16) as u16 } } #[cfg(target_endian = "little")] pub fn ntohs(u: u16) -> u16 { unsafe { intrinsics::bswap16(u as i16) as u16 } } fn addr_to_sockaddr(addr: ip::SocketAddr) -> (libc::sockaddr_storage, uint) { unsafe { let storage: libc::sockaddr_storage = intrinsics::init(); let len = match addr.ip { ip::Ipv4Addr(a, b, c, d) => { 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.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(&storage); (*storage).sin6_family = libc::AF_INET6 as libc::sa_family_t; (*storage).sin6_port = htons(addr.port); (*storage).sin6_addr.s6_addr[0] = htons(a); (*storage).sin6_addr.s6_addr[1] = htons(b); (*storage).sin6_addr.s6_addr[2] = htons(c); (*storage).sin6_addr.s6_addr[3] = htons(d); (*storage).sin6_addr.s6_addr[4] = htons(e); (*storage).sin6_addr.s6_addr[5] = htons(f); (*storage).sin6_addr.s6_addr[6] = htons(g); (*storage).sin6_addr.s6_addr[7] = htons(h); mem::size_of::() } }; return (storage, len); } } fn socket(addr: ip::SocketAddr) -> IoResult { unsafe { let fam = match addr.ip { ip::Ipv4Addr(..) => libc::AF_INET, ip::Ipv6Addr(..) => libc::AF_INET6, }; match libc::socket(fam, libc::SOCK_STREAM, 0) { -1 => Err(super::last_error()), fd => Ok(fd), } } } fn sockname(fd: sock_t, f: extern "system" unsafe fn(sock_t, *mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int) -> IoResult { let mut storage: libc::sockaddr_storage = unsafe { intrinsics::init() }; let mut len = mem::size_of::() as libc::socklen_t; unsafe { let storage = &mut storage as *mut libc::sockaddr_storage; let ret = f(fd, storage as *mut libc::sockaddr, &mut len as *mut libc::socklen_t); if ret != 0 { return Err(super::last_error()) } } match storage.ss_family as libc::c_int { libc::AF_INET => { assert!(len as uint >= mem::size_of::()); let storage: &mut libc::sockaddr_in = unsafe { cast::transmute(&mut 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: &mut libc::sockaddr_in6 = unsafe { cast::transmute(&mut 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(unix)] pub fn init() {} #[cfg(windows)] pub fn init() { static WSADESCRIPTION_LEN: uint = 256; static WSASYS_STATUS_LEN: uint = 128; struct WSADATA { wVersion: libc::WORD, wHighVersion: libc::WORD, szDescription: [u8, ..WSADESCRIPTION_LEN + 1], szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1], iMaxSockets: u16, iMaxUdpDg: u16, lpVendorInfo: *u8, } type LPWSADATA = *mut WSADATA; #[link(name = "ws2_32")] extern "system" { fn WSAStartup(wVersionRequested: libc::WORD, lpWSAData: LPWSADATA) -> libc::c_int; } unsafe { use std::unstable::mutex::{Mutex, MUTEX_INIT}; static mut LOCK: Mutex = MUTEX_INIT; static mut INITIALIZED: bool = false; if INITIALIZED { return } LOCK.lock(); if !INITIALIZED { let mut data: WSADATA = intrinsics::init(); let ret = WSAStartup(0x202, // version 2.2 &mut data); assert_eq!(ret, 0); INITIALIZED = true; } LOCK.unlock(); } } impl TcpStream { pub fn connect(addr: ip::SocketAddr) -> IoResult { unsafe { socket(addr).and_then(|fd| { let (addr, len) = addr_to_sockaddr(addr); let addrp = &addr as *libc::sockaddr_storage; let ret = TcpStream { fd: fd }; match libc::connect(fd, addrp as *libc::sockaddr, len as libc::socklen_t) { -1 => Err(super::last_error()), _ => Ok(ret), } }) } } pub fn fd(&self) -> sock_t { self.fd } fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> { unsafe { let on = nodelay as libc::c_int; let on = &on as *libc::c_int; super::mkerr_libc(libc::setsockopt(self.fd, libc::IPPROTO_TCP, libc::TCP_NODELAY, on as *libc::c_void, mem::size_of::() as libc::socklen_t)) } } fn set_keepalive(&mut self, seconds: Option) -> IoResult<()> { unsafe { let on = seconds.is_some() as libc::c_int; let on = &on as *libc::c_int; let ret = libc::setsockopt(self.fd, libc::SOL_SOCKET, libc::SO_KEEPALIVE, on as *libc::c_void, mem::size_of::() as libc::socklen_t); if ret != 0 { return Err(super::last_error()) } match seconds { Some(n) => self.set_tcp_keepalive(n), None => Ok(()) } } } #[cfg(target_os = "macos")] unsafe fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> { let delay = seconds as libc::c_uint; let delay = &delay as *libc::c_uint; let ret = libc::setsockopt(self.fd, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, delay as *libc::c_void, mem::size_of::() as libc::socklen_t); super::mkerr_libc(ret) } #[cfg(target_os = "freebsd")] unsafe fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> { let delay = seconds as libc::c_uint; let delay = &delay as *libc::c_uint; let ret = libc::setsockopt(self.fd, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, delay as *libc::c_void, mem::size_of::() as libc::socklen_t); super::mkerr_libc(ret) } #[cfg(not(target_os = "macos"), not(target_os = "freebsd"))] unsafe fn set_tcp_keepalive(&mut self, _seconds: uint) -> IoResult<()> { Ok(()) } } #[cfg(windows)] type wrlen = libc::c_int; #[cfg(not(windows))] type wrlen = libc::size_t; impl rtio::RtioTcpStream for TcpStream { fn read(&mut self, buf: &mut [u8]) -> IoResult { let ret = keep_going(buf, |buf, len| { unsafe { libc::recv(self.fd, buf as *mut libc::c_void, len as wrlen, 0) as i64 } }); if ret == 0 { Err(io::standard_error(io::EndOfFile)) } else if ret < 0 { Err(super::last_error()) } else { Ok(ret as uint) } } fn write(&mut self, buf: &[u8]) -> IoResult<()> { let ret = keep_going(buf, |buf, len| { unsafe { libc::send(self.fd, buf as *mut libc::c_void, len as wrlen, 0) as i64 } }); if ret < 0 { Err(super::last_error()) } else { Ok(()) } } fn peer_name(&mut self) -> IoResult { sockname(self.fd, libc::getpeername) } fn control_congestion(&mut self) -> IoResult<()> { self.set_nodelay(false) } fn nodelay(&mut self) -> IoResult<()> { self.set_nodelay(true) } fn keepalive(&mut self, delay_in_seconds: uint) -> IoResult<()> { self.set_keepalive(Some(delay_in_seconds)) } fn letdie(&mut self) -> IoResult<()> { self.set_keepalive(None) } } impl rtio::RtioSocket for TcpStream { fn socket_name(&mut self) -> IoResult { sockname(self.fd, libc::getsockname) } } impl Drop for TcpStream { #[cfg(unix)] fn drop(&mut self) { unsafe { libc::close(self.fd); } } #[cfg(windows)] fn drop(&mut self) { unsafe { libc::closesocket(self.fd); } } } pub struct TcpListener { priv fd: sock_t, } impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult { unsafe { socket(addr).and_then(|fd| { let (addr, len) = addr_to_sockaddr(addr); let addrp = &addr as *libc::sockaddr_storage; let ret = TcpListener { fd: fd }; match libc::bind(fd, addrp as *libc::sockaddr, len as libc::socklen_t) { -1 => Err(super::last_error()), _ => Ok(ret), } }) } } pub fn fd(&self) -> sock_t { self.fd } pub fn native_listen(self, backlog: int) -> IoResult { match unsafe { libc::listen(self.fd, backlog as libc::c_int) } { -1 => Err(super::last_error()), _ => Ok(TcpAcceptor { fd: self.fd }) } } } impl rtio::RtioTcpListener for TcpListener { fn listen(~self) -> IoResult<~rtio::RtioTcpAcceptor> { self.native_listen(128).map(|a| ~a as ~rtio::RtioTcpAcceptor) } } impl rtio::RtioSocket for TcpListener { fn socket_name(&mut self) -> IoResult { sockname(self.fd, libc::getsockname) } } pub struct TcpAcceptor { priv fd: sock_t, } impl TcpAcceptor { pub fn fd(&self) -> sock_t { self.fd } pub fn native_accept(&mut self) -> IoResult { unsafe { let mut storage: libc::sockaddr_storage = intrinsics::init(); let storagep = &mut storage as *mut libc::sockaddr_storage; let size = mem::size_of::(); let mut size = size as libc::socklen_t; match libc::accept(self.fd, storagep as *mut libc::sockaddr, &mut size as *mut libc::socklen_t) { -1 => Err(super::last_error()), fd => Ok(TcpStream { fd: fd }) } } } } impl rtio::RtioSocket for TcpAcceptor { fn socket_name(&mut self) -> IoResult { sockname(self.fd, libc::getsockname) } } impl rtio::RtioTcpAcceptor for TcpAcceptor { fn accept(&mut self) -> IoResult<~rtio::RtioTcpStream> { self.native_accept().map(|s| ~s as ~rtio::RtioTcpStream) } fn accept_simultaneously(&mut self) -> IoResult<()> { Ok(()) } fn dont_accept_simultaneously(&mut self) -> IoResult<()> { Ok(()) } }