Created
August 13, 2025 17:27
-
-
Save lffg/79b259581ef5b7dbf9d53e9da0a222e9 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| 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