support for checking fingerprint, misc
This commit is contained in:
		
							
								
								
									
										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));
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								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)),
 | 
				
			||||||
            } else {
 | 
					                    None => {
 | 
				
			||||||
                        // No mapped address
 | 
					                        // No mapped address
 | 
				
			||||||
                        std::process::exit(1);
 | 
					                        std::process::exit(1);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                println!("{}", cli.format.format_stun(&msg));
 | 
					                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:?}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user