Last active
August 29, 2015 14:18
-
-
Save GGist/e3fe849a17da2487395d to your computer and use it in GitHub Desktop.
Binding A UdpSocket As SO_REUSEADDR To Listen On A Multicast Address (UPnP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(udp, ip_addr, libc)] | |
extern crate libc; | |
use std::io::{Result, Error, ErrorKind}; | |
use std::net::{UdpSocket, ToSocketAddrs, SocketAddr, Ipv4Addr, IpAddr}; | |
use std::mem; | |
#[cfg(windows)] | |
pub type SockT = libc::SOCKET; | |
#[cfg(not(windows))] | |
pub type SockT = libc::c_int; | |
fn main() { | |
let udp = bind_reuse(("192.168.2.102", 1900)).unwrap(); | |
println!("{}", transmute_fd(&udp)); | |
let search = &b"M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nST: ssdp:all\r\nMX: 3\r\n\r\n"[..]; | |
let ip = IpAddr::V4(Ipv4Addr::new(239, 255, 255, 250)); | |
udp.join_multicast(&ip).unwrap(); | |
udp.send_to(search, (ip, 1900)).unwrap(); | |
for _ in 0.. { | |
let mut b = [0u8; 800]; | |
let (_, addr) = udp.recv_from(&mut b[..]).unwrap(); | |
println!("{:?}", addr); | |
for i in b.iter() { | |
print!("{}", *i as char); | |
} | |
print!("\n"); | |
} | |
} | |
/// Performs a transmute on the UdpSocket in order to return the internal file | |
/// descriptor of the underlying socket. | |
fn transmute_fd(udp: &UdpSocket) -> SockT { | |
unsafe{ mem::transmute_copy::<UdpSocket, SockT>(udp) } | |
} | |
/// Bind A UdpSocket To The Given Address With The SO_REUSEADDR Option Set. | |
fn bind_reuse<A: ToSocketAddrs>(addr: A) -> Result<UdpSocket> { | |
let mut ret; | |
// Dummy UdpSocket Will Run Socket Initialization Code For Process Since | |
// We Can't Access The init() Function Ourselves (Private Visibility) | |
let _ = try!(UdpSocket::bind(("0.0.0.0", 0))); | |
let socket_addr = try!(try!(addr.to_socket_addrs()).next().ok_or( | |
Error::new(ErrorKind::InvalidInput, "Error With Addr Passed In") | |
)); | |
// Create Socket | |
let family = match socket_addr { | |
SocketAddr::V4(..) => libc::AF_INET, | |
SocketAddr::V6(..) => libc::AF_INET6 | |
}; | |
let sock: SockT = unsafe{ libc::socket(family, libc::SOCK_DGRAM, 0) }; | |
try!(check_sock(sock)); | |
// Set SO_REUSEADDR On Socket | |
ret = unsafe{ libc::setsockopt(sock, libc::SOL_SOCKET, libc::SO_REUSEADDR, | |
&1i32 as &libc::c_int as *const libc::c_int as *const libc::c_void, | |
mem::size_of::<libc::c_int>() as libc::socklen_t) | |
}; | |
if ret != 0 { | |
return Err(Error::last_os_error()) | |
} | |
// Bind Address On Socket | |
let (sock_addr, len) = match socket_addr { | |
SocketAddr::V4(ref a) => | |
(a as *const _ as *const _, mem::size_of_val(a) as libc::socklen_t), | |
SocketAddr::V6(ref a) => | |
(a as *const _ as *const _, mem::size_of_val(a) as libc::socklen_t) | |
}; | |
ret = unsafe{ libc::bind(sock, sock_addr, len) }; | |
if ret != 0 { | |
return Err(Error::last_os_error()) | |
} | |
// LOL I HAVE NO IDEA WHY THIS WORKS!!!!!!! | |
// Joking Aside, I Looked At The Bit Patterns For A Bunch Of UdpSockets I | |
// Created And Even Though They All Had A 32 Bit Representation Under The | |
// Hood (Either SOCKET Or c_int), The Size Of The UdpSocket Was 64 Bits. | |
// All Of The Upper Bits For The UdpSocket Had The Same Bit Pattern Which Was | |
// 0000 0000 0000 0000 0000 0000 1101 0100 .... Or In Decimal 910533066752 | |
// So That Is What We Are Setting The Upper Bits To Below. | |
let size_correction: u64 = 910533066752 | (sock as u64); | |
// Return New Socket | |
Ok(unsafe{ mem::transmute::<u64, UdpSocket>(size_correction) }) | |
} | |
/// Check The Return Value Of A Call To libc::socket(). | |
#[cfg(windows)] | |
fn check_sock(sock: SockT) -> Result<()> { | |
if sock == libc::INVALID_SOCKET { | |
// Dont Have Access To Private Function WSAGetLastError() | |
Err(Error::new(ErrorKind::Other, "Error With Socket Creation")) | |
} else { | |
Ok(()) | |
} | |
} | |
/// Check The Return Value Of A Call To libc::socket(). | |
#[cfg(not(windows))] | |
fn check_sock(sock: SockT) -> Result<()> { | |
if sock == -1i32 { | |
Err(Error::last_os_error()) | |
} else { | |
Ok(()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment