support for checking fingerprint, misc
This commit is contained in:
parent
6985ed6614
commit
a66a6a263e
76
src/lib.rs
76
src/lib.rs
@ -1,4 +1,4 @@
|
|||||||
use crc32fast::hash;
|
use crc32fast::{hash, Hasher};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use nom::bytes::complete::{tag, take};
|
use nom::bytes::complete::{tag, take};
|
||||||
use nom::error::ParseError;
|
use nom::error::ParseError;
|
||||||
@ -144,7 +144,7 @@ pub enum StunAttribute {
|
|||||||
ChangedAddress(AddrPort),
|
ChangedAddress(AddrPort),
|
||||||
Username(String),
|
Username(String),
|
||||||
MessageIntegrity([u8; 20]),
|
MessageIntegrity([u8; 20]),
|
||||||
Fingerprint(u32),
|
Fingerprint((u32, bool)),
|
||||||
ErrorCode((u16, String)),
|
ErrorCode((u16, String)),
|
||||||
Realm(String),
|
Realm(String),
|
||||||
Nonce(String),
|
Nonce(String),
|
||||||
@ -202,22 +202,27 @@ impl fmt::Display for StunAttribute {
|
|||||||
a.port
|
a.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::Username(username) => writeln!(f, " Username {}", username),
|
StunAttribute::Username(username) => write!(f, " Username {}", username),
|
||||||
StunAttribute::MessageIntegrity(msg_integrity) => {
|
StunAttribute::MessageIntegrity(msg_integrity) => {
|
||||||
write!(f, " MessageIntegrity {:?}", msg_integrity)
|
write!(f, " MessageIntegrity {:?}", msg_integrity)
|
||||||
}
|
}
|
||||||
StunAttribute::Fingerprint(fingerprint) => {
|
StunAttribute::Fingerprint((crc, ok)) => {
|
||||||
write!(f, " Fingerprint 0x{:08x}", fingerprint)
|
write!(
|
||||||
|
f,
|
||||||
|
" Fingerprint 0x{:08x} ({})",
|
||||||
|
crc,
|
||||||
|
if *ok { "OK" } else { "FAIL" }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::ErrorCode((err_num, error)) => {
|
StunAttribute::ErrorCode((err_num, error)) => {
|
||||||
write!(f, " ErrorCode {} ({})", err_num, error)
|
write!(f, " ErrorCode {} ({})", err_num, error)
|
||||||
}
|
}
|
||||||
StunAttribute::Realm(realm) => writeln!(f, " Realm {}", realm),
|
StunAttribute::Realm(realm) => write!(f, " Realm {}", realm),
|
||||||
StunAttribute::Nonce(nonce) => writeln!(f, " Nonce {}", nonce),
|
StunAttribute::Nonce(nonce) => write!(f, " Nonce {}", nonce),
|
||||||
StunAttribute::UnknownAttributes(unknown_attrs) => {
|
StunAttribute::UnknownAttributes(unknown_attrs) => {
|
||||||
write!(f, " UnknownAttributes {:?}", unknown_attrs)
|
write!(f, " UnknownAttributes {:?}", unknown_attrs)
|
||||||
}
|
}
|
||||||
StunAttribute::Software(software) => writeln!(f, " Software {}", software),
|
StunAttribute::Software(software) => write!(f, " Software {}", software),
|
||||||
StunAttribute::AlternateServer(a) => {
|
StunAttribute::AlternateServer(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
@ -235,7 +240,7 @@ impl fmt::Display for StunAttribute {
|
|||||||
a.address,
|
a.address,
|
||||||
a.port
|
a.port
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
StunAttribute::OtherAddress(a) => {
|
StunAttribute::OtherAddress(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
@ -244,7 +249,7 @@ impl fmt::Display for StunAttribute {
|
|||||||
a.address,
|
a.address,
|
||||||
a.port
|
a.port
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
StunAttribute::Unknown((attr_type, data)) => {
|
StunAttribute::Unknown((attr_type, data)) => {
|
||||||
write!(f, " Unknown ({:04x}) {:?}", attr_type, data)
|
write!(f, " Unknown ({:04x}) {:?}", attr_type, data)
|
||||||
}
|
}
|
||||||
@ -334,16 +339,59 @@ where
|
|||||||
|
|
||||||
fn parse_stun_message<'a, I, E: ParseError<I>>(input: I) -> IResult<I, StunMessage, E>
|
fn parse_stun_message<'a, I, E: ParseError<I>>(input: I) -> IResult<I, StunMessage, E>
|
||||||
where
|
where
|
||||||
I: nom::Input<Item = u8> + nom::Compare<I> + nom::Compare<&'a [u8]> + AsBytes + Debug,
|
I: nom::Input<Item = u8>
|
||||||
|
+ nom::Compare<I>
|
||||||
|
+ nom::Compare<&'a [u8]>
|
||||||
|
+ nom::Offset
|
||||||
|
+ AsBytes
|
||||||
|
+ Debug,
|
||||||
{
|
{
|
||||||
|
let mut hasher = Some(Hasher::new());
|
||||||
|
let input_start = input.clone();
|
||||||
|
|
||||||
let (input, h) = parse_stun_header(input)?;
|
let (input, h) = parse_stun_header(input)?;
|
||||||
|
hasher
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.update(input_start.take(input_start.offset(&input)).as_bytes());
|
||||||
|
|
||||||
let (residual, input) = take(h.msg_length)(input)?;
|
let (residual, input) = take(h.msg_length)(input)?;
|
||||||
if residual.input_len() != 0 {
|
if residual.input_len() != 0 {
|
||||||
warn!("Trailing bytes in STUN message: {:?}", residual);
|
warn!("Trailing bytes in STUN message: {:?}", residual);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (input, attributes) = many0(parse_stun_attribute(&h.tx_id)).parse(input)?;
|
let mut input = input;
|
||||||
if !input.input_len() != 0 {
|
let mut attributes = Vec::new();
|
||||||
|
|
||||||
|
while let Ok((new_input, attr)) = parse_stun_attribute::<I, E>(&h.tx_id)(input.clone()) {
|
||||||
|
let attr = if let Some(StunAttribute::Fingerprint(fingerprint)) = attr {
|
||||||
|
let crc = hasher.unwrap().finalize() ^ 0x5354554e;
|
||||||
|
hasher = None;
|
||||||
|
if crc == fingerprint.0 {
|
||||||
|
Some(StunAttribute::Fingerprint((crc, true)))
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Fingerprint mismatch: expected 0x{:08x}, got 0x{:08x}",
|
||||||
|
crc, fingerprint.0
|
||||||
|
);
|
||||||
|
attr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if hasher.is_some() {
|
||||||
|
hasher
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.update(input.take(input.offset(&new_input)).as_bytes());
|
||||||
|
} else {
|
||||||
|
warn!("Received attributes after FINGERPRINT");
|
||||||
|
}
|
||||||
|
attr
|
||||||
|
};
|
||||||
|
|
||||||
|
attributes.push(attr);
|
||||||
|
input = new_input;
|
||||||
|
}
|
||||||
|
if input.input_len() != 0 {
|
||||||
warn!("Trailing bytes in STUN message attributes: {:?}", input);
|
warn!("Trailing bytes in STUN message attributes: {:?}", input);
|
||||||
}
|
}
|
||||||
let attributes = StunAttributes(attributes.iter().filter_map(|i| i.clone()).collect());
|
let attributes = StunAttributes(attributes.iter().filter_map(|i| i.clone()).collect());
|
||||||
@ -492,7 +540,7 @@ where
|
|||||||
}
|
}
|
||||||
ATTR_NUM_FINGERPRINT => {
|
ATTR_NUM_FINGERPRINT => {
|
||||||
let (_residual, fingerprint) = be_u32(attr_data)?;
|
let (_residual, fingerprint) = be_u32(attr_data)?;
|
||||||
StunAttribute::Fingerprint(fingerprint)
|
StunAttribute::Fingerprint((fingerprint, false))
|
||||||
}
|
}
|
||||||
ATTR_REALM => {
|
ATTR_REALM => {
|
||||||
let realm = String::from_iter(attr_data.iter_elements().map(|b| b as char));
|
let realm = String::from_iter(attr_data.iter_elements().map(|b| b as char));
|
||||||
|
33
src/main.rs
33
src/main.rs
@ -42,7 +42,7 @@ impl OutputFormat {
|
|||||||
#[command(about = "Test a Tailscale derp node's stun service")]
|
#[command(about = "Test a Tailscale derp node's stun service")]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
host: String,
|
host: String,
|
||||||
#[clap(short, long, default_value = "3478")]
|
#[clap(default_value = "3478")]
|
||||||
port: u16,
|
port: u16,
|
||||||
#[clap(short = '4', conflicts_with = "v6_only", default_value = "false")]
|
#[clap(short = '4', conflicts_with = "v6_only", default_value = "false")]
|
||||||
v4_only: bool,
|
v4_only: bool,
|
||||||
@ -100,22 +100,27 @@ fn main() {
|
|||||||
|
|
||||||
let mut buf = [0u8; 1500];
|
let mut buf = [0u8; 1500];
|
||||||
|
|
||||||
if let Ok(received) = socket.recv(&mut buf) {
|
match socket.recv(&mut buf) {
|
||||||
debug!("Received response: {:?}", &buf[..received]);
|
Ok(received) => {
|
||||||
|
let buf = &buf[..received];
|
||||||
|
debug!("Received response: {:?}", buf);
|
||||||
|
|
||||||
let msg = StunMessage::parse(&buf).unwrap();
|
let msg = StunMessage::parse(buf).unwrap();
|
||||||
info!("Parsed message from {}:", socket.peer_addr().unwrap());
|
info!("Parsed message from {}:", socket.peer_addr().unwrap());
|
||||||
if cli.address_only {
|
if cli.address_only {
|
||||||
if let Some(addr) = msg.attributes.mapped_address() {
|
match msg.attributes.mapped_address() {
|
||||||
println!("{}", cli.format.format_address(addr));
|
Some(addr) => println!("{}", cli.format.format_address(addr)),
|
||||||
|
None => {
|
||||||
|
// No mapped address
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No mapped address
|
println!("{}", cli.format.format_stun(&msg));
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
println!("{}", cli.format.format_stun(&msg));
|
|
||||||
}
|
}
|
||||||
} else if let Err(e) = socket.recv(&mut buf) {
|
Err(e) => {
|
||||||
println!("recv function failed: {e:?}");
|
println!("recv function failed: {e:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user