Skip to content

Commit 3487811

Browse files
committed
examples: add an example stun client
Originally from https://github.com/ystreet/librice v0.0.5
1 parent 2641e37 commit 3487811

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

stun-types/examples/stunclient.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
use std::env;
10+
use std::net::{SocketAddr, UdpSocket};
11+
use std::process::exit;
12+
use std::str::FromStr;
13+
use std::io::{Read, Write};
14+
use std::net::TcpStream;
15+
16+
use tracing_subscriber::EnvFilter;
17+
18+
use tracing::{info, trace};
19+
20+
use stun_types::TransportType;
21+
use stun_types::attribute::*;
22+
use stun_types::message::*;
23+
24+
fn usage() {
25+
println!("stunclient [protocol] [address:port]");
26+
println!();
27+
println!("\tprotocol: can be either \'udp\' or \'tcp\'");
28+
}
29+
30+
fn parse_response(response: Message) -> Result<(), std::io::Error> {
31+
if Message::check_attribute_types(
32+
&response,
33+
&[XorMappedAddress::TYPE, Fingerprint::TYPE, AttributeType::new(1)],
34+
&[XorMappedAddress::TYPE],
35+
)
36+
.is_some()
37+
{
38+
return Err(std::io::Error::new(
39+
std::io::ErrorKind::InvalidData,
40+
"Required attributes not found in response",
41+
));
42+
}
43+
if response.has_class(MessageClass::Success) {
44+
// presence checked by check_attribute_types() above
45+
let mapped_address = response.attribute::<XorMappedAddress>().unwrap();
46+
let visible_addr = mapped_address.addr(response.transaction_id());
47+
println!("found visible address {:?}", visible_addr);
48+
Ok(())
49+
} else if response.has_class(MessageClass::Error) {
50+
println!("got error response {:?}", response);
51+
Err(std::io::Error::new(
52+
std::io::ErrorKind::Other,
53+
"Error response",
54+
))
55+
} else {
56+
println!("got unknown response {:?}", response);
57+
Err(std::io::Error::new(
58+
std::io::ErrorKind::Other,
59+
"Unknown response",
60+
))
61+
}
62+
}
63+
64+
fn tcp_message(out: MessageBuilder<'_>, to: SocketAddr) -> Result<(), std::io::Error> {
65+
let mut socket = TcpStream::connect(to).unwrap();
66+
67+
info!("generated to {:?}", out);
68+
let buf = out.build();
69+
trace!("generated to {:?}", buf);
70+
socket.write_all(&buf)?;
71+
let mut buf = [0; 1500];
72+
let amt = socket.read(&mut buf)?;
73+
let buf = &buf[..amt];
74+
trace!("got {:?}", buf);
75+
let msg = Message::from_bytes(buf)
76+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, format!("Invalid message: {e:?}")))?;
77+
info!(
78+
"received from {:?} to {:?} {}",
79+
socket.peer_addr().unwrap(),
80+
socket.local_addr().unwrap(),
81+
msg
82+
);
83+
84+
parse_response(msg)
85+
}
86+
87+
fn udp_message(out: MessageBuilder<'_>, to: SocketAddr) -> Result<(), std::io::Error> {
88+
let socket = UdpSocket::bind("0.0.0.0:0")?;
89+
90+
info!("generated to {:?}", out);
91+
let buf = out.build();
92+
trace!("generated to {:?}", buf);
93+
socket.send_to(&buf, to)?;
94+
let mut buf = [0; 1500];
95+
let (amt, src) = socket.recv_from(&mut buf)?;
96+
let buf = &buf[..amt];
97+
trace!("got {:?}", buf);
98+
let msg = Message::from_bytes(buf)
99+
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid message"))?;
100+
info!(
101+
"got from {:?} to {:?} {}",
102+
src,
103+
socket.local_addr().unwrap(),
104+
msg
105+
);
106+
107+
parse_response(msg)
108+
}
109+
110+
fn main() -> std::io::Result<()> {
111+
if let Ok(filter) = EnvFilter::try_from_default_env() {
112+
tracing_subscriber::fmt().with_env_filter(filter).init();
113+
}
114+
115+
let args: Vec<String> = env::args().collect();
116+
let proto = if args.len() > 1 {
117+
if args[1] == "udp" {
118+
TransportType::Udp
119+
} else if args[1] == "tcp" {
120+
TransportType::Tcp
121+
} else {
122+
usage();
123+
exit(1);
124+
}
125+
} else {
126+
TransportType::Udp
127+
};
128+
129+
let to: SocketAddr = SocketAddr::from_str(if args.len() > 2 {
130+
&args[2]
131+
} else {
132+
"127.0.0.1:3478"
133+
})
134+
.unwrap();
135+
136+
println!("sending STUN message over {:?} to {}", proto, to);
137+
let mut msg = Message::builder_request(BINDING);
138+
msg.add_fingerprint().unwrap();
139+
140+
match proto {
141+
TransportType::Udp => udp_message(msg, to),
142+
TransportType::Tcp => tcp_message(msg, to),
143+
}
144+
}

0 commit comments

Comments
 (0)