Skip to content

Commit ab6af69

Browse files
committed
proto: add a minimal stund example
Very simple and does not do resource limits at all.
1 parent 39f5d83 commit ab6af69

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

stun-proto/examples/stund.rs

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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::fmt::Display;
10+
use std::net::SocketAddr;
11+
12+
use std::io::{self, Read, Write};
13+
use std::net::{TcpListener, UdpSocket};
14+
use std::str::FromStr;
15+
16+
use tracing_subscriber::EnvFilter;
17+
18+
use tracing::{debug, error, info, warn};
19+
20+
use stun_types::attribute::*;
21+
use stun_types::message::*;
22+
23+
use stun_proto::agent::{HandleStunReply, StunAgent, StunError};
24+
25+
fn warn_on_err<T, E>(res: Result<T, E>, default: T) -> T
26+
where
27+
E: Display,
28+
{
29+
match res {
30+
Ok(v) => v,
31+
Err(e) => {
32+
warn!("{}", e);
33+
default
34+
}
35+
}
36+
}
37+
38+
fn handle_binding_request<'a>(
39+
msg: &Message,
40+
from: SocketAddr,
41+
) -> Result<MessageBuilder<'a>, StunError> {
42+
if let Some(error_msg) = Message::check_attribute_types(msg, &[Fingerprint::TYPE], &[]) {
43+
return Ok(error_msg);
44+
}
45+
46+
let mut response = Message::builder_success(msg);
47+
response.add_attribute(&XorMappedAddress::new(from, msg.transaction_id()))?;
48+
response.add_fingerprint()?;
49+
Ok(response)
50+
}
51+
52+
fn handle_incoming_data<'a>(
53+
data: &[u8],
54+
from: SocketAddr,
55+
stun_agent: &mut StunAgent,
56+
) -> Option<(MessageBuilder<'a>, SocketAddr)> {
57+
let msg = Message::from_bytes(data).ok()?;
58+
let reply = stun_agent.handle_stun(msg, from);
59+
match reply {
60+
HandleStunReply::Drop => None,
61+
HandleStunReply::StunResponse(_response) => {
62+
error!("received unexpected STUN response from {from}!");
63+
None
64+
}
65+
HandleStunReply::IncomingStun(msg) => {
66+
info!("received from {}: {}", from, msg);
67+
if msg.has_class(MessageClass::Request) && msg.has_method(BINDING) {
68+
match handle_binding_request(&msg, from) {
69+
Ok(response) => {
70+
info!("sending response to {}: {:?}", from, response);
71+
return Some((response, from));
72+
}
73+
Err(err) => warn!("error: {}", err),
74+
}
75+
} else {
76+
let mut response = Message::builder_error(&msg);
77+
response
78+
.add_attribute(&ErrorCode::new(400, "Bad Request").unwrap())
79+
.unwrap();
80+
return Some((response, from));
81+
}
82+
None
83+
}
84+
}
85+
}
86+
87+
fn main() -> io::Result<()> {
88+
if let Ok(filter) = EnvFilter::try_from_default_env() {
89+
tracing_subscriber::fmt().with_env_filter(filter).init();
90+
}
91+
92+
let args: Vec<String> = std::env::args().collect();
93+
let local_addr: SocketAddr = SocketAddr::from_str(if args.len() > 1 {
94+
&args[1]
95+
} else {
96+
"127.0.0.1:3478"
97+
})
98+
.unwrap();
99+
std::thread::spawn({
100+
move || {
101+
let udp_socket = UdpSocket::bind(local_addr).unwrap();
102+
let mut udp_stun_agent =
103+
StunAgent::builder(stun_proto::types::TransportType::Udp, local_addr).build();
104+
105+
loop {
106+
let mut data = [0; 1500];
107+
let (len, from) = warn_on_err(udp_socket.recv_from(&mut data), (0, local_addr));
108+
if let Some((response, to)) =
109+
handle_incoming_data(&data[..len], from, &mut udp_stun_agent)
110+
{
111+
warn_on_err(udp_socket.send_to(&response.build(), to), 0);
112+
}
113+
}
114+
}
115+
});
116+
117+
let tcp_listener = TcpListener::bind(local_addr)?;
118+
let mut incoming = tcp_listener.incoming();
119+
while let Some(Ok(mut stream)) = incoming.next() {
120+
std::thread::spawn(move || {
121+
let remote_addr = stream.peer_addr().unwrap();
122+
let mut tcp_stun_agent =
123+
StunAgent::builder(stun_proto::types::TransportType::Tcp, local_addr)
124+
.remote_addr(remote_addr)
125+
.build();
126+
// TODO: handle split writes/reads and request timeouts.
127+
let mut data = [0; 1500];
128+
let size = warn_on_err(stream.read(&mut data), 0);
129+
if size == 0 {
130+
debug!("TCP connection with {remote_addr} closed");
131+
return;
132+
}
133+
if let Some((response, to)) =
134+
handle_incoming_data(&data[..size], remote_addr, &mut tcp_stun_agent)
135+
{
136+
if let Ok(data) = tcp_stun_agent.send(response, to) {
137+
warn_on_err(stream.write_all(&data.data), ());
138+
}
139+
}
140+
// XXX: Assumes that the stun packet arrives in a single packet
141+
stream.shutdown(std::net::Shutdown::Read).unwrap();
142+
});
143+
}
144+
145+
Ok(())
146+
}

0 commit comments

Comments
 (0)