diff --git a/src/lib.rs b/src/lib.rs index adad073b0..aa50ddc0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ extern crate log; mod readiness_stream; mod event_loop; mod tcp; +mod udp; #[path = "../../src/slot.rs"] mod slot; #[path = "../../src/lock.rs"] @@ -30,3 +31,4 @@ mod lock; pub use event_loop::{Loop, LoopHandle}; pub use readiness_stream::ReadinessStream; pub use tcp::{TcpListener, TcpStream}; +pub use udp::UdpSocket; diff --git a/src/udp.rs b/src/udp.rs new file mode 100644 index 000000000..1b5f4995a --- /dev/null +++ b/src/udp.rs @@ -0,0 +1,238 @@ +use std::io; +use std::net::{self, SocketAddr, Ipv4Addr, Ipv6Addr}; +use std::sync::Arc; + +use futures::stream::Stream; +use futures::{Future, failed, Task, Poll}; +use futures_io::{Ready, IoFuture}; +use mio; + +use {ReadinessStream, LoopHandle}; +use event_loop::Source; + + +/// An I/O object representing a UDP socket. +pub struct UdpSocket { + source: Arc>, + ready: ReadinessStream, +} + +impl LoopHandle { + /// Create a new UDP socket bound to the specified address. + /// + /// This function will create a new UDP socket and attempt to bind it to the + /// `addr` provided. The returned future will be resolved once the socket + /// has successfully bound. If an error happens during the binding or during + /// the socket creation, that error will be returned to the future instead. + pub fn udp_bind(self, addr: &SocketAddr) -> Box> { + match mio::udp::UdpSocket::bind(addr) { + Ok(udp) => UdpSocket::new(udp, self), + Err(e) => failed(e).boxed(), + } + } +} + +impl UdpSocket { + fn new(socket: mio::udp::UdpSocket, handle: LoopHandle) + -> Box> { + let socket = Arc::new(Source::new(socket)); + ReadinessStream::new(handle, socket.clone()).map(|ready| { + UdpSocket { + source: socket, + ready: ready, + } + }).boxed() + } + + /// Creates a new `UdpSocket` from the previously bound socket provided. + /// + /// The socket given will be registered with the event loop that `handle` is + /// associated with. This function requires that `socket` has previously + /// been bound to an address to work correctly. + /// + /// This can be used in conjunction with net2's `UdpBuilder` interface to + /// configure a socket before it's handed off, such as setting options like + /// `reuse_address` or binding to multiple addresses. + pub fn from_socket(socket: net::UdpSocket, + handle: LoopHandle) -> Box> { + match mio::udp::UdpSocket::from_socket(socket) { + Ok(tcp) => UdpSocket::new(tcp, handle), + Err(e) => failed(e).boxed(), + } + } + + /// Returns the local address that this stream is bound to. + pub fn local_addr(&self) -> io::Result { + self.source.io().local_addr() + } + + /// Sends data on the socket to the given address. On success, returns the + /// number of bytes written. + /// + /// Address type can be any implementor of `ToSocketAddrs` trait. See its + /// documentation for concrete examples. + pub fn send_to(&self, buf: &[u8], target: &SocketAddr) + -> io::Result> { + self.source.io().send_to(buf, target) + } + + /// Receives data from the socket. On success, returns the number of bytes + /// read and the address from whence the data came. + pub fn recv_from(&self, buf: &mut [u8]) + -> io::Result> { + self.source.io().recv_from(buf) + } + + /// Gets the value of the `SO_BROADCAST` option for this socket. + /// + /// For more information about this option, see + /// [`set_broadcast`][link]. + /// + /// [link]: #method.set_broadcast + pub fn broadcast(&self) -> io::Result { + self.source.io().broadcast() + } + + /// Sets the value of the `SO_BROADCAST` option for this socket. + /// + /// When enabled, this socket is allowed to send packets to a broadcast + /// address. + pub fn set_broadcast(&self, on: bool) -> io::Result<()> { + self.source.io().set_broadcast(on) + } + + /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// For more information about this option, see + /// [`set_multicast_loop_v4`][link]. + /// + /// [link]: #method.set_multicast_loop_v4 + pub fn multicast_loop_v4(&self) -> io::Result { + self.source.io().multicast_loop_v4() + } + + /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. + /// + /// If enabled, multicast packets will be looped back to the local socket. + /// Note that this may not have any affect on IPv6 sockets. + pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> { + self.source.io().set_multicast_loop_v4(on) + } + + /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// For more information about this option, see + /// [`set_multicast_ttl_v4`][link]. + /// + /// [link]: #method.set_multicast_ttl_v4 + pub fn multicast_ttl_v4(&self) -> io::Result { + self.source.io().multicast_ttl_v4() + } + + /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. + /// + /// Indicates the time-to-live value of outgoing multicast packets for + /// this socket. The default value is 1 which means that multicast packets + /// don't leave the local network unless explicitly requested. + /// + /// Note that this may not have any affect on IPv6 sockets. + pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { + self.source.io().set_multicast_ttl_v4(ttl) + } + + /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// For more information about this option, see + /// [`set_multicast_loop_v6`][link]. + /// + /// [link]: #method.set_multicast_loop_v6 + pub fn multicast_loop_v6(&self) -> io::Result { + self.source.io().multicast_loop_v6() + } + + /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. + /// + /// Controls whether this socket sees the multicast packets it sends itself. + /// Note that this may not have any affect on IPv4 sockets. + pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> { + self.source.io().set_multicast_loop_v6(on) + } + + /// Gets the value of the `IP_TTL` option for this socket. + /// + /// For more information about this option, see [`set_ttl`][link]. + /// + /// [link]: #method.set_ttl + pub fn ttl(&self) -> io::Result { + self.source.io().ttl() + } + + /// Sets the value for the `IP_TTL` option on this socket. + /// + /// This value sets the time-to-live field that is used in every packet sent + /// from this socket. + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + self.source.io().set_ttl(ttl) + } + + /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and `interface` is the + /// address of the local interface with which the system should join the + /// multicast group. If it's equal to `INADDR_ANY` then an appropriate + /// interface is chosen by the system. + pub fn join_multicast_v4(&self, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr) -> io::Result<()> { + self.source.io().join_multicast_v4(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. + /// + /// This function specifies a new multicast group for this socket to join. + /// The address must be a valid multicast address, and `interface` is the + /// index of the interface to join/leave (or 0 to indicate any interface). + pub fn join_multicast_v6(&self, + multiaddr: &Ipv6Addr, + interface: u32) -> io::Result<()> { + self.source.io().join_multicast_v6(multiaddr, interface) + } + + /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. + /// + /// For more information about this option, see + /// [`join_multicast_v4`][link]. + /// + /// [link]: #method.join_multicast_v4 + pub fn leave_multicast_v4(&self, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr) -> io::Result<()> { + self.source.io().leave_multicast_v4(multiaddr, interface) + } + + /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. + /// + /// For more information about this option, see + /// [`join_multicast_v6`][link]. + /// + /// [link]: #method.join_multicast_v6 + pub fn leave_multicast_v6(&self, + multiaddr: &Ipv6Addr, + interface: u32) -> io::Result<()> { + self.source.io().leave_multicast_v6(multiaddr, interface) + } +} + +impl Stream for UdpSocket { + type Item = Ready; + type Error = io::Error; + + fn poll(&mut self, task: &mut Task) -> Poll, io::Error> { + self.ready.poll(task) + } + + fn schedule(&mut self, task: &mut Task) { + self.ready.schedule(task) + } +}