Skip to content

Commit 93045bf

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

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

stun-types/examples/stunclient.rs

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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::io::{Read, Write};
11+
use std::net::TcpStream;
12+
use std::net::{SocketAddr, UdpSocket};
13+
use std::process::exit;
14+
use std::str::FromStr;
15+
16+
use tracing_subscriber::EnvFilter;
17+
18+
use tracing::{info, trace};
19+
20+
use stun_types::attribute::*;
21+
use stun_types::message::*;
22+
use stun_types::TransportType;
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+
&[
34+
XorMappedAddress::TYPE,
35+
Fingerprint::TYPE,
36+
AttributeType::new(1),
37+
],
38+
&[XorMappedAddress::TYPE],
39+
)
40+
.is_some()
41+
{
42+
return Err(std::io::Error::new(
43+
std::io::ErrorKind::InvalidData,
44+
"Required attributes not found in response",
45+
));
46+
}
47+
if response.has_class(MessageClass::Success) {
48+
// presence checked by check_attribute_types() above
49+
let mapped_address = response.attribute::<XorMappedAddress>().unwrap();
50+
let visible_addr = mapped_address.addr(response.transaction_id());
51+
println!("found visible address {:?}", visible_addr);
52+
Ok(())
53+
} else if response.has_class(MessageClass::Error) {
54+
println!("got error response {:?}", response);
55+
Err(std::io::Error::new(
56+
std::io::ErrorKind::Other,
57+
"Error response",
58+
))
59+
} else {
60+
println!("got unknown response {:?}", response);
61+
Err(std::io::Error::new(
62+
std::io::ErrorKind::Other,
63+
"Unknown response",
64+
))
65+
}
66+
}
67+
68+
fn tcp_message(out: MessageBuilder<'_>, to: SocketAddr) -> Result<(), std::io::Error> {
69+
let mut socket = TcpStream::connect(to).unwrap();
70+
71+
info!("generated to {:?}", out);
72+
let buf = out.build();
73+
trace!("generated to {:?}", buf);
74+
socket.write_all(&buf)?;
75+
let mut buf = [0; 1500];
76+
let amt = socket.read(&mut buf)?;
77+
let buf = &buf[..amt];
78+
trace!("got {:?}", buf);
79+
let msg = Message::from_bytes(buf).map_err(|e| {
80+
std::io::Error::new(
81+
std::io::ErrorKind::InvalidData,
82+
format!("Invalid message: {e:?}"),
83+
)
84+
})?;
85+
info!(
86+
"received from {:?} to {:?} {}",
87+
socket.peer_addr().unwrap(),
88+
socket.local_addr().unwrap(),
89+
msg
90+
);
91+
92+
parse_response(msg)
93+
}
94+
95+
fn udp_message(out: MessageBuilder<'_>, to: SocketAddr) -> Result<(), std::io::Error> {
96+
let socket = UdpSocket::bind("0.0.0.0:0")?;
97+
98+
info!("generated to {:?}", out);
99+
let buf = out.build();
100+
trace!("generated to {:?}", buf);
101+
socket.send_to(&buf, to)?;
102+
let mut buf = [0; 1500];
103+
let (amt, src) = socket.recv_from(&mut buf)?;
104+
let buf = &buf[..amt];
105+
trace!("got {:?}", buf);
106+
let msg = Message::from_bytes(buf)
107+
.map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid message"))?;
108+
info!(
109+
"got from {:?} to {:?} {}",
110+
src,
111+
socket.local_addr().unwrap(),
112+
msg
113+
);
114+
115+
parse_response(msg)
116+
}
117+
118+
fn main() -> std::io::Result<()> {
119+
if let Ok(filter) = EnvFilter::try_from_default_env() {
120+
tracing_subscriber::fmt().with_env_filter(filter).init();
121+
}
122+
123+
let args: Vec<String> = env::args().collect();
124+
let proto = if args.len() > 1 {
125+
if args[1] == "udp" {
126+
TransportType::Udp
127+
} else if args[1] == "tcp" {
128+
TransportType::Tcp
129+
} else {
130+
usage();
131+
exit(1);
132+
}
133+
} else {
134+
TransportType::Udp
135+
};
136+
137+
let to: SocketAddr = SocketAddr::from_str(if args.len() > 2 {
138+
&args[2]
139+
} else {
140+
"127.0.0.1:3478"
141+
})
142+
.unwrap();
143+
144+
println!("sending STUN message over {:?} to {}", proto, to);
145+
let mut msg = Message::builder_request(BINDING);
146+
msg.add_fingerprint().unwrap();
147+
148+
match proto {
149+
TransportType::Udp => udp_message(msg, to),
150+
TransportType::Tcp => tcp_message(msg, to),
151+
}
152+
}

0 commit comments

Comments
 (0)