Skip to content

Instantly share code, notes, and snippets.

@GGist
Last active August 29, 2015 14:18
Show Gist options
  • Save GGist/e3fe849a17da2487395d to your computer and use it in GitHub Desktop.
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)
#![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