refactor address output, add -4, -6 AF flags
This commit is contained in:
parent
a3e944d1d6
commit
903bf23646
163
src/lib.rs
163
src/lib.rs
@ -1,11 +1,10 @@
|
|||||||
use crc32fast::hash;
|
use crc32fast::hash;
|
||||||
use log::{debug, info, warn};
|
use log::warn;
|
||||||
use nom::bytes::complete::{tag, take};
|
use nom::bytes::complete::{tag, take};
|
||||||
use nom::error::ParseError;
|
use nom::error::ParseError;
|
||||||
use nom::multi::{many, many0};
|
use nom::multi::{many, many0};
|
||||||
use nom::number::complete::{be_u128, be_u16, be_u32, be_u8};
|
use nom::number::complete::{be_u128, be_u16, be_u32, be_u8};
|
||||||
use nom::{AsBytes, IResult, Parser};
|
use nom::{AsBytes, IResult, Parser};
|
||||||
use rand::seq::{IndexedRandom, SliceRandom};
|
|
||||||
use rand::{self, RngCore};
|
use rand::{self, RngCore};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
@ -42,6 +41,12 @@ const HEADER_LEN: u16 = 20;
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TxId([u8; 12]);
|
pub struct TxId([u8; 12]);
|
||||||
|
|
||||||
|
impl Default for TxId {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TxId {
|
impl TxId {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut tx_id = [0; 12];
|
let mut tx_id = [0; 12];
|
||||||
@ -101,6 +106,21 @@ fn fingerprint(msg: &[u8]) -> u32 {
|
|||||||
hash(msg) ^ 0x5354554e
|
hash(msg) ^ 0x5354554e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct AddrPort {
|
||||||
|
pub address: IpAddr,
|
||||||
|
pub port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(IpAddr, u16)> for AddrPort {
|
||||||
|
fn from(value: (IpAddr, u16)) -> Self {
|
||||||
|
Self {
|
||||||
|
address: value.0,
|
||||||
|
port: value.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize)]
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
pub enum StunClass {
|
pub enum StunClass {
|
||||||
Request = 0,
|
Request = 0,
|
||||||
@ -116,10 +136,10 @@ pub enum StunMethod {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub enum StunAttribute {
|
pub enum StunAttribute {
|
||||||
MappedAddress((IpAddr, u16)),
|
MappedAddress(AddrPort),
|
||||||
XorMappedAddress((IpAddr, u16)),
|
XorMappedAddress(AddrPort),
|
||||||
SourceAddress((IpAddr, u16)),
|
SourceAddress(AddrPort),
|
||||||
ChangedAddress((IpAddr, u16)),
|
ChangedAddress(AddrPort),
|
||||||
Username(String),
|
Username(String),
|
||||||
MessageIntegrity([u8; 20]),
|
MessageIntegrity([u8; 20]),
|
||||||
Fingerprint(u32),
|
Fingerprint(u32),
|
||||||
@ -128,7 +148,7 @@ pub enum StunAttribute {
|
|||||||
Nonce(String),
|
Nonce(String),
|
||||||
UnknownAttributes(Vec<u16>),
|
UnknownAttributes(Vec<u16>),
|
||||||
Software(String),
|
Software(String),
|
||||||
AlternateServer((IpAddr, u16)),
|
AlternateServer(AddrPort),
|
||||||
Unknown((u16, Vec<u8>)),
|
Unknown((u16, Vec<u8>)),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,40 +162,40 @@ fn addr_family(addr: &IpAddr) -> &'static str {
|
|||||||
impl fmt::Display for StunAttribute {
|
impl fmt::Display for StunAttribute {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
StunAttribute::MappedAddress((addr, port)) => {
|
StunAttribute::MappedAddress(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
" MappedAddress ({}) {}:{}",
|
" MappedAddress ({}) {}:{}",
|
||||||
addr_family(addr),
|
addr_family(&a.address),
|
||||||
addr,
|
a.address,
|
||||||
port
|
a.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::SourceAddress((addr, port)) => {
|
StunAttribute::SourceAddress(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
" SourceAddress ({}) {}:{}",
|
" SourceAddress ({}) {}:{}",
|
||||||
addr_family(addr),
|
addr_family(&a.address),
|
||||||
addr,
|
a.address,
|
||||||
port
|
a.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::ChangedAddress((addr, port)) => {
|
StunAttribute::ChangedAddress(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
" ChangedAddress ({}) {}:{}",
|
" ChangedAddress ({}) {}:{}",
|
||||||
addr_family(addr),
|
addr_family(&a.address),
|
||||||
addr,
|
a.address,
|
||||||
port
|
a.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::XorMappedAddress((addr, port)) => {
|
StunAttribute::XorMappedAddress(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
" XorMappedAddress ({}) {}:{}",
|
" XorMappedAddress ({}) {}:{}",
|
||||||
addr_family(addr),
|
addr_family(&a.address),
|
||||||
addr,
|
a.address,
|
||||||
port
|
a.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::Username(username) => writeln!(f, " Username {}", username),
|
StunAttribute::Username(username) => writeln!(f, " Username {}", username),
|
||||||
@ -194,13 +214,13 @@ impl fmt::Display for StunAttribute {
|
|||||||
write!(f, " UnknownAttributes {:?}", unknown_attrs)
|
write!(f, " UnknownAttributes {:?}", unknown_attrs)
|
||||||
}
|
}
|
||||||
StunAttribute::Software(software) => writeln!(f, " Software {}", software),
|
StunAttribute::Software(software) => writeln!(f, " Software {}", software),
|
||||||
StunAttribute::AlternateServer((addr, port)) => {
|
StunAttribute::AlternateServer(a) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
" AlternateServer ({}) {}:{}",
|
" AlternateServer ({}) {}:{}",
|
||||||
addr_family(addr),
|
addr_family(&a.address),
|
||||||
addr,
|
a.address,
|
||||||
port
|
a.port
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StunAttribute::Unknown((attr_type, data)) => {
|
StunAttribute::Unknown((attr_type, data)) => {
|
||||||
@ -240,7 +260,6 @@ impl fmt::Display for StunHeader {
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct StunAttributes(Vec<StunAttribute>);
|
pub struct StunAttributes(Vec<StunAttribute>);
|
||||||
|
|
||||||
|
|
||||||
impl fmt::Display for StunAttributes {
|
impl fmt::Display for StunAttributes {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f, " Attributes")?;
|
writeln!(f, " Attributes")?;
|
||||||
@ -252,9 +271,11 @@ impl fmt::Display for StunAttributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StunAttributes {
|
impl StunAttributes {
|
||||||
pub fn mapped_address(&self) -> Option<&(IpAddr, u16)> {
|
pub fn mapped_address(&self) -> Option<&AddrPort> {
|
||||||
self.0.iter().find_map(|attr| match attr {
|
self.0.iter().find_map(|attr| match attr {
|
||||||
StunAttribute::MappedAddress(addr) | StunAttribute::XorMappedAddress(addr) => Some(addr),
|
StunAttribute::MappedAddress(addr) | StunAttribute::XorMappedAddress(addr) => {
|
||||||
|
Some(addr)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -274,7 +295,6 @@ impl fmt::Display for StunMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl StunMessage {
|
impl StunMessage {
|
||||||
pub fn parse(bytes: &[u8]) -> Result<Self, nom::Err<nom::error::Error<&[u8]>>> {
|
pub fn parse(bytes: &[u8]) -> Result<Self, nom::Err<nom::error::Error<&[u8]>>> {
|
||||||
let (_, msg) = parse_stun_message(bytes)?;
|
let (_, msg) = parse_stun_message(bytes)?;
|
||||||
@ -282,27 +302,27 @@ impl StunMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_txid<I, E: ParseError<I>>(bytes: I) -> IResult<I, TxId, E>
|
fn take_txid<I, E: ParseError<I>>(input: I) -> IResult<I, TxId, E>
|
||||||
where
|
where
|
||||||
I: nom::Input<Item = u8> + AsBytes,
|
I: nom::Input<Item = u8> + AsBytes,
|
||||||
{
|
{
|
||||||
let (bytes, tx_id) = take(12usize)(bytes)?;
|
let (input, tx_id) = take(12usize)(input)?;
|
||||||
Ok((bytes, TxId::from_bytes(tx_id.as_bytes())))
|
Ok((input, TxId::from_bytes(tx_id.as_bytes())))
|
||||||
}
|
}
|
||||||
|
|
||||||
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]> + AsBytes + Debug,
|
||||||
{
|
{
|
||||||
let (bytes, h) = parse_stun_header(input)?;
|
let (input, h) = parse_stun_header(input)?;
|
||||||
let (residual, bytes) = take(h.msg_length)(bytes)?;
|
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 (bytes, attributes) = many0(parse_stun_attribute(&h.tx_id)).parse(bytes)?;
|
let (input, attributes) = many0(parse_stun_attribute(&h.tx_id)).parse(input)?;
|
||||||
if !bytes.input_len() != 0 {
|
if !input.input_len() != 0 {
|
||||||
warn!("Trailing bytes in STUN message attributes: {:?}", bytes);
|
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());
|
||||||
|
|
||||||
@ -315,10 +335,10 @@ where
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stun_message_type<'a, I: nom::Input<Item = u8>, E: ParseError<I>>(
|
fn parse_stun_message_type<I: nom::Input<Item = u8>, E: ParseError<I>>(
|
||||||
input: I,
|
input: I,
|
||||||
) -> IResult<I, StunMessageType, E> {
|
) -> IResult<I, StunMessageType, E> {
|
||||||
let (bytes, msg_type_raw) = be_u16(input)?;
|
let (input, msg_type_raw) = be_u16(input)?;
|
||||||
if msg_type_raw & 0b11000000 != 0 {
|
if msg_type_raw & 0b11000000 != 0 {
|
||||||
panic!("Invalid STUN message type");
|
panic!("Invalid STUN message type");
|
||||||
}
|
}
|
||||||
@ -335,39 +355,36 @@ fn parse_stun_message_type<'a, I: nom::Input<Item = u8>, E: ParseError<I>>(
|
|||||||
1 => StunMethod::Binding,
|
1 => StunMethod::Binding,
|
||||||
_ => panic!("Invalid STUN message method"),
|
_ => panic!("Invalid STUN message method"),
|
||||||
};
|
};
|
||||||
Ok((bytes, StunMessageType { class, method }))
|
Ok((input, StunMessageType { class, method }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stun_address<I: nom::Input<Item = u8>, E: ParseError<I>>(
|
fn parse_stun_address<I: nom::Input<Item = u8>, E: ParseError<I>>(
|
||||||
bytes: I,
|
input: I,
|
||||||
) -> IResult<I, (IpAddr, u16), E> {
|
) -> IResult<I, AddrPort, E> {
|
||||||
let (bytes, _) = take(1usize)(bytes)?;
|
let (input, _) = take(1usize)(input)?;
|
||||||
let (bytes, family) = be_u8(bytes)?;
|
let (input, family) = be_u8(input)?;
|
||||||
let (bytes, port) = be_u16(bytes)?;
|
let (input, port) = be_u16(input)?;
|
||||||
let (bytes, addr) = match family {
|
let (input, addr) = match family {
|
||||||
0x01 => {
|
0x01 => {
|
||||||
let (bytes, val) = be_u32(bytes)?;
|
let (input, val) = be_u32(input)?;
|
||||||
(bytes, (IpAddr::V4(val.into()), port))
|
(input, (IpAddr::V4(val.into()), port).into())
|
||||||
}
|
}
|
||||||
0x02 => {
|
0x02 => {
|
||||||
let (bytes, val) = be_u128(bytes)?;
|
let (input, val) = be_u128(input)?;
|
||||||
(bytes, (IpAddr::V6(val.into()), port))
|
(input, (IpAddr::V6(val.into()), port).into())
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid address family"),
|
_ => panic!("Invalid address family"),
|
||||||
};
|
};
|
||||||
Ok((bytes, addr))
|
Ok((input, addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stun_xor_address<I, E: ParseError<I>>(
|
fn parse_stun_xor_address<I, E: ParseError<I>>(input: I, tx_id: &TxId) -> IResult<I, AddrPort, E>
|
||||||
bytes: I,
|
|
||||||
tx_id: &TxId,
|
|
||||||
) -> IResult<I, (IpAddr, u16), E>
|
|
||||||
where
|
where
|
||||||
I: nom::Input<Item = u8>,
|
I: nom::Input<Item = u8>,
|
||||||
{
|
{
|
||||||
let (bytes, addr) = parse_stun_address(bytes)?;
|
let (input, addr) = parse_stun_address(input)?;
|
||||||
let xor_port = addr.1 ^ 0x2112;
|
let xor_port = addr.port ^ 0x2112;
|
||||||
let xor_addr = match addr.0 {
|
let xor_addr = match addr.address {
|
||||||
IpAddr::V4(v4) => {
|
IpAddr::V4(v4) => {
|
||||||
let v4 = u32::from(v4);
|
let v4 = u32::from(v4);
|
||||||
let xor_v4 = v4 ^ 0x2112a442;
|
let xor_v4 = v4 ^ 0x2112a442;
|
||||||
@ -379,7 +396,7 @@ where
|
|||||||
IpAddr::V6(xor_v6.into())
|
IpAddr::V6(xor_v6.into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok((bytes, (xor_addr, xor_port)))
|
Ok((input, (xor_addr, xor_port).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stun_attribute<I, E: ParseError<I>>(
|
fn parse_stun_attribute<I, E: ParseError<I>>(
|
||||||
@ -389,22 +406,22 @@ where
|
|||||||
I: nom::Input<Item = u8> + nom::Compare<I> + AsBytes,
|
I: nom::Input<Item = u8> + nom::Compare<I> + AsBytes,
|
||||||
{
|
{
|
||||||
let tx_id = tx_id.clone();
|
let tx_id = tx_id.clone();
|
||||||
move |bytes| parse_stun_attribute_impl(bytes, &tx_id)
|
move |input| parse_stun_attribute_impl(input, &tx_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stun_attribute_impl<I, E: ParseError<I>>(
|
fn parse_stun_attribute_impl<I, E: ParseError<I>>(
|
||||||
bytes: I,
|
input: I,
|
||||||
tx_id: &TxId,
|
tx_id: &TxId,
|
||||||
) -> IResult<I, Option<StunAttribute>, E>
|
) -> IResult<I, Option<StunAttribute>, E>
|
||||||
where
|
where
|
||||||
I: nom::Input<Item = u8> + nom::Compare<I> + AsBytes,
|
I: nom::Input<Item = u8> + nom::Compare<I> + AsBytes,
|
||||||
{
|
{
|
||||||
let (bytes, attr_type) = be_u16(bytes)?;
|
let (input, attr_type) = be_u16(input)?;
|
||||||
let (bytes, attr_len) = be_u16(bytes)?;
|
let (input, attr_len) = be_u16(input)?;
|
||||||
let (bytes, attr_data) = take(attr_len)(bytes)?;
|
let (input, attr_data) = take(attr_len)(input)?;
|
||||||
|
|
||||||
if attr_len == 0 {
|
if attr_len == 0 {
|
||||||
return Ok((bytes, None));
|
return Ok((input, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let attr = match attr_type {
|
let attr = match attr_type {
|
||||||
@ -478,19 +495,19 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((bytes, Some(attr)))
|
Ok((input, Some(attr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stun_header<'a, I, E: ParseError<I>>(input: I) -> IResult<I, StunHeader, E>
|
fn parse_stun_header<'a, I, E: ParseError<I>>(input: I) -> IResult<I, StunHeader, E>
|
||||||
where
|
where
|
||||||
I: nom::Input<Item = u8> + nom::Compare<I> + nom::Compare<&'a [u8]> + AsBytes,
|
I: nom::Input<Item = u8> + nom::Compare<I> + nom::Compare<&'a [u8]> + AsBytes,
|
||||||
{
|
{
|
||||||
let (bytes, msg_type) = parse_stun_message_type(input)?;
|
let (input, msg_type) = parse_stun_message_type(input)?;
|
||||||
let (bytes, msg_length) = be_u16(bytes)?;
|
let (input, msg_length) = be_u16(input)?;
|
||||||
let (bytes, _) = tag(MAGIC_COOKIE.as_bytes())(bytes)?;
|
let (input, _) = tag(MAGIC_COOKIE.as_bytes())(input)?;
|
||||||
let (bytes, tx_id) = take_txid(bytes)?;
|
let (input, tx_id) = take_txid(input)?;
|
||||||
Ok((
|
Ok((
|
||||||
bytes,
|
input,
|
||||||
StunHeader {
|
StunHeader {
|
||||||
msg_type,
|
msg_type,
|
||||||
msg_length,
|
msg_length,
|
||||||
|
47
src/main.rs
47
src/main.rs
@ -1,8 +1,7 @@
|
|||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info};
|
||||||
use serde::Serialize;
|
use std::net::{IpAddr, ToSocketAddrs, UdpSocket};
|
||||||
use std::net::{IpAddr, UdpSocket};
|
use tailstun::{AddrPort, StunMessage, TxId};
|
||||||
use tailstun::{StunMessage, TxId};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, ValueEnum)]
|
#[derive(Debug, Clone, ValueEnum)]
|
||||||
enum OutputFormat {
|
enum OutputFormat {
|
||||||
@ -19,11 +18,21 @@ impl OutputFormat {
|
|||||||
OutputFormat::Yaml => serde_yaml::to_string(msg).unwrap(),
|
OutputFormat::Yaml => serde_yaml::to_string(msg).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn format_address(&self, (addr, port): &(IpAddr, u16)) -> String {
|
fn format_address(&self, a: &AddrPort) -> String {
|
||||||
|
let a = match a.address {
|
||||||
|
IpAddr::V4(_) => a.address,
|
||||||
|
IpAddr::V6(v6) => {
|
||||||
|
if let Some(v4) = v6.to_ipv4_mapped() {
|
||||||
|
IpAddr::V4(v4)
|
||||||
|
} else {
|
||||||
|
a.address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
match self {
|
match self {
|
||||||
OutputFormat::Text => format!("{}", addr),
|
OutputFormat::Text => format!("{}", a),
|
||||||
OutputFormat::Json => serde_json::to_string_pretty(addr).unwrap(),
|
OutputFormat::Json => serde_json::to_string_pretty(&a).unwrap(),
|
||||||
OutputFormat::Yaml => serde_yaml::to_string(addr).unwrap(),
|
OutputFormat::Yaml => serde_yaml::to_string(&a).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,13 +44,17 @@ struct Cli {
|
|||||||
host: String,
|
host: String,
|
||||||
#[clap(short, long, default_value = "3478")]
|
#[clap(short, long, default_value = "3478")]
|
||||||
port: u16,
|
port: u16,
|
||||||
|
#[clap(short = '4', conflicts_with = "v6_only", default_value = "false")]
|
||||||
|
v4_only: bool,
|
||||||
|
#[clap(short = '6', conflicts_with = "v4_only", default_value = "false")]
|
||||||
|
v6_only: bool,
|
||||||
#[clap(short, long, default_value = "text")]
|
#[clap(short, long, default_value = "text")]
|
||||||
format: OutputFormat,
|
format: OutputFormat,
|
||||||
#[clap(
|
#[clap(
|
||||||
short,
|
short,
|
||||||
long,
|
long,
|
||||||
default_value = "false",
|
default_value = "false",
|
||||||
help = "Only output the first mapped address"
|
help = "Only output the first mapped address & convert IPv6-mapped to IPv4"
|
||||||
)]
|
)]
|
||||||
address_only: bool,
|
address_only: bool,
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
@ -54,7 +67,20 @@ fn main() {
|
|||||||
.filter_level(cli.verbose.log_level_filter())
|
.filter_level(cli.verbose.log_level_filter())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let dest = cli.host + ":" + &cli.port.to_string();
|
let dest = (cli.host.as_str(), cli.port)
|
||||||
|
.to_socket_addrs()
|
||||||
|
.expect("Unable to resolve host")
|
||||||
|
.find(|a| {
|
||||||
|
if cli.v4_only {
|
||||||
|
a.is_ipv4()
|
||||||
|
} else if cli.v6_only {
|
||||||
|
a.is_ipv6()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("No address found for host");
|
||||||
|
|
||||||
let socket = UdpSocket::bind("[::]:0").expect("Unable to bind a UDP socket");
|
let socket = UdpSocket::bind("[::]:0").expect("Unable to bind a UDP socket");
|
||||||
socket
|
socket
|
||||||
.connect(dest)
|
.connect(dest)
|
||||||
@ -83,6 +109,7 @@ fn main() {
|
|||||||
if let Some(addr) = msg.attributes.mapped_address() {
|
if let Some(addr) = msg.attributes.mapped_address() {
|
||||||
println!("{}", cli.format.format_address(addr));
|
println!("{}", cli.format.format_address(addr));
|
||||||
} else {
|
} else {
|
||||||
|
// No mapped address
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user