Skip to content

Instantly share code, notes, and snippets.

@lffg
Created August 13, 2025 17:27
Show Gist options
  • Select an option

  • Save lffg/79b259581ef5b7dbf9d53e9da0a222e9 to your computer and use it in GitHub Desktop.

Select an option

Save lffg/79b259581ef5b7dbf9d53e9da0a222e9 to your computer and use it in GitHub Desktop.
use std::io;
use std::time::Duration;
use std::{
io::Write,
net::{SocketAddr, ToSocketAddrs, UdpSocket},
str::FromStr,
};
use stun_rs::{
MessageClass, MessageDecoderBuilder, MessageEncoderBuilder, StunMessageBuilder,
attributes::stun::XorMappedAddress, methods::BINDING,
};
const DEFAULT_STUN_SERVER: &str = "stun.l.google.com:19302";
fn main() {
let stun_addr = dns_resolve(DEFAULT_STUN_SERVER);
println!("Resolved address: {stun_addr}");
let mut soc = UdpSocket::bind("0.0.0.0:0").unwrap();
println!("{}", soc.local_addr().unwrap());
let xor_addr = get_xor_addr(DEFAULT_STUN_SERVER, &mut soc);
println!("My external address: {xor_addr}");
let my_name: String = input("My name: ");
let peer_addr: SocketAddr = input("Enter peer address: ");
let mut buf = vec![0; 512];
soc.connect(peer_addr).unwrap();
println!("Connected to peer! ({peer_addr})");
std::thread::scope(|s| {
s.spawn({
let soc = soc.try_clone().unwrap();
move || {
loop {
std::thread::sleep(Duration::from_secs(1));
let Ok((size, peer_addr2)) = soc.recv_from(&mut buf) else {
print!("Failed to receive");
continue;
};
let msg_utf8 = String::from_utf8_lossy(&mut buf[..size]);
println!("Got message from {peer_addr2}: [{msg_utf8}]");
}
}
});
s.spawn(|| {
let mut i = 1;
loop {
let msg = format!("from {my_name}: {i}");
soc.send(msg.as_bytes()).unwrap();
i += 1;
std::thread::sleep(Duration::from_secs(1));
}
});
});
}
fn dns_resolve(addr: impl ToSocketAddrs) -> SocketAddr {
addr.to_socket_addrs().unwrap().next().expect("empty addr")
}
fn get_xor_addr(addr: impl ToSocketAddrs, soc: &mut UdpSocket) -> SocketAddr {
let msg = StunMessageBuilder::new(BINDING, MessageClass::Request).build();
let encoder = MessageEncoderBuilder::default().build();
let mut buf = vec![0; 256];
let size = encoder.encode(&mut buf, &msg).unwrap();
soc.connect(addr).unwrap();
println!("Connected to stun server");
soc.send(&buf[..size]).unwrap();
println!("Message sent");
let size = soc.recv(&mut buf).unwrap();
println!("Got {size} bytes from server");
let decoder = MessageDecoderBuilder::default().build();
let (msg, decoded_size) = decoder.decode(&buf[..size]).unwrap();
assert_eq!(decoded_size, size);
*msg.get::<XorMappedAddress>()
.expect("Missing XorMappedAddress")
.as_xor_mapped_address()
.expect("unreachable: we asked for this variant above")
.socket_address()
}
fn input<T>(prompt: &str) -> T
where
T: FromStr,
T::Err: std::fmt::Debug,
{
print!("{prompt}");
io::stdout().flush().unwrap();
let mut buf = String::new();
io::stdin().read_line(&mut buf).unwrap();
buf.trim().parse().unwrap()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment