use std::fs::OpenOptions;
use std::io::{stdout, BufWriter, Result as ioResult, Write};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs, UdpSocket};
use std::path::PathBuf;
use std::thread::{Builder, JoinHandle};
use net2::UdpBuilder;
const BUFSIZE: usize = 8096;
pub fn upstream_socket_interface(listen_addr: String) -> ioResult<(SocketAddr, UdpSocket)> {
let addr = listen_addr
.to_socket_addrs()
.unwrap()
.next()
.expect("parsing socket address");
let listen_socket;
match (addr.ip().is_multicast(), addr.ip()) {
(false, std::net::IpAddr::V4(_)) => {
listen_socket = UdpSocket::bind(addr).expect("binding server socket");
}
(false, std::net::IpAddr::V6(_)) => {
listen_socket = UdpSocket::bind(addr).expect("binding server socket");
}
(true, std::net::IpAddr::V4(ip)) => {
#[cfg(not(target_os = "windows"))]
{
listen_socket = UdpBuilder::new_v4()
.expect("binding ipv4 socket")
.reuse_address(true)
.unwrap()
.bind(addr)
.unwrap();
listen_socket
.join_multicast_v4(&ip, &Ipv4Addr::UNSPECIFIED)
.unwrap_or_else(|e| panic!("{}", e));
}
#[cfg(target_os = "windows")]
{
listen_socket = UdpSocket::bind(SocketAddr::new(
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
addr.port(),
))
.expect("binding server socket");
listen_socket
.join_multicast_v4(&ip, &Ipv4Addr::UNSPECIFIED)
.unwrap_or_else(|e| panic!("{}", e));
}
}
(true, std::net::IpAddr::V6(ip)) => {
listen_socket = UdpBuilder::new_v6()
.expect("binding ipv6 socket")
.reuse_address(true)
.unwrap()
.bind(SocketAddr::new(
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
addr.port(),
))
.unwrap();
#[cfg(not(target_os = "macos"))]
let itf = 0; #[cfg(target_os = "macos")]
let itf = 12;
listen_socket
.join_multicast_v6(&ip, itf)
.unwrap_or_else(|e| panic!("{}", e));
#[cfg(target_os = "windows")]
listen_socket
.connect(&addr)
.unwrap_or_else(|e| panic!("{}", e));
}
};
Ok((addr, listen_socket))
}
pub fn listener(addr: String, logfile: PathBuf, tee: bool) -> JoinHandle<()> {
let file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(&logfile);
let mut writer = BufWriter::new(file.unwrap());
let mut output_buffer = BufWriter::new(stdout());
let (addr, listen_socket) = upstream_socket_interface(addr).unwrap();
Builder::new()
.name(format!("{}:server", addr))
.spawn(move || {
let mut buf = [0u8; BUFSIZE]; loop {
match listen_socket.recv_from(&mut buf[0..]) {
Ok((c, _remote_addr)) => {
if tee {
let _o = output_buffer
.write(&buf[0..c])
.expect("writing to output buffer");
#[cfg(debug_assertions)]
assert!(c == _o);
}
let _ = writer
.write(&buf[0..c])
.unwrap_or_else(|_| panic!("writing to {:?}", &logfile));
}
Err(err) => {
writer.flush().unwrap();
eprintln!("{}:server: got an error: {}", addr, err);
#[cfg(debug_assertions)]
panic!("{}:server: got an error: {}", addr, err);
}
}
writer.flush().unwrap();
if tee {
output_buffer.flush().unwrap();
}
}
})
.unwrap()
}