├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs ├── main.rs └── valid_ipv4s.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nat-detect" 3 | version = "0.1.7" 4 | edition = "2021" 5 | authors = ["blazh "] 6 | keywords = ["nat", "stun", "detect"] 7 | readme = "README.md" 8 | license = "MIT" 9 | description = "a simple nat detect implementation for rust" 10 | repository = "https://github.com/blazood/nat-detect" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | tokio = { version = "1.17", features = ["full"] } 16 | stun_codec_blazh= {version="0.1.13"} 17 | log = "0.4" 18 | simple_logger = "2.1" 19 | clap = { version = "3.1", features = ["derive"] } 20 | rand = "0.8.5" 21 | bytecodec = "0.4.15" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 blazood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nat-detect 2 | > a simple nat detect implementation for rust 3 | 4 | 5 | ```text 6 | +--------+ 7 | | Test | 8 | | I | 9 | +--------+ 10 | | 11 | | 12 | V 13 | /\ /\ 14 | N / \ Y / \ Y +--------+ 15 | UDP <-------/Resp\--------->/ IP \------------->| Test | 16 | Blocked \ ? / \Same/ | II | 17 | \ / \? / +--------+ 18 | \/ \/ | 19 | | N | 20 | | V 21 | V /\ 22 | +--------+ Sym. N / \ 23 | | Test | UDP <---/Resp\ 24 | | II | Firewall \ ? / 25 | +--------+ \ / 26 | | \/ 27 | V |Y 28 | /\ /\ | 29 | Symmetric N / \ +--------+ N / \ V 30 | NAT <--- / IP \<-----| Test |<--- /Resp\ Open 31 | \Same/ | I | \ ? / Internet 32 | \? / +--------+ \ / 33 | \/ \/ 34 | | |Y 35 | | | 36 | | V 37 | | Full 38 | | Cone 39 | V /\ 40 | +--------+ / \ Y 41 | | Test |------>/Resp\---->Restricted 42 | | III | \ ? / 43 | +--------+ \ / 44 | \/ 45 | |N 46 | | Port 47 | +------>Restricted 48 | ``` 49 | 50 | **Install** 51 | ```bash 52 | cargo install nat-detect 53 | ``` 54 | 55 | **Usage** 56 | ``` 57 | # use default stun server list 58 | # may be, these default servers will not good work in your net! 59 | nat-detect 60 | 61 | # use stun server 193.43.148.37:3478 62 | nat-detect -s 193.43.148.37:3478 63 | ``` 64 | 65 | 66 | 67 | default stun server list use [always-online-stun](https://github.com/pradt2/always-online-stun) -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::io::{ErrorKind}; 3 | use std::net::{IpAddr, Ipv4Addr, SocketAddr}; 4 | use tokio::net::UdpSocket; 5 | use std::time::Duration; 6 | 7 | 8 | use log::{debug, info}; 9 | use bytecodec::{DecodeExt, EncodeExt}; 10 | use stun_codec_blazh::{Attribute, Message, MessageClass, MessageDecoder, MessageEncoder, TransactionId}; 11 | use stun_codec_blazh::rfc3489::attributes::ChangedAddress; 12 | use stun_codec_blazh::rfc5389::attributes::{MappedAddress, UnknownAttributes}; 13 | use stun_codec_blazh::rfc5389::methods; 14 | use stun_codec_blazh::rfc5780::attributes::ChangeRequest; 15 | use crate::NatType::{FullCone, OpenInternet, PortRestrictedCone, RestrictedCone, Symmetric, SymmetricUdpFirewall, Unknown}; 16 | 17 | 18 | type IoResult= std::io::Result; 19 | 20 | // pub(crate) const FAMILY_IPV4: u16 = 0x01; 21 | // pub(crate) const FAMILY_IPV6: u16 = 0x02; 22 | // pub(crate) const IPV4LEN: usize = 4; 23 | // pub(crate) const IPV6LEN: usize = 16; 24 | 25 | pub const TIMEOUT: Duration = Duration::from_millis(1000); 26 | 27 | pub const STUN_RETRY_COUNT: usize = 2; 28 | 29 | #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] 30 | pub enum NatType{ 31 | 32 | /// UDP is always blocked. 33 | UdpBlocked, 34 | 35 | /// No NAT, public IP, no firewall. 36 | OpenInternet, 37 | 38 | /// No NAT, public IP, but symmetric UDP firewall. 39 | SymmetricUdpFirewall, 40 | 41 | /// A full cone NAT is one where all requests from the same internal IP address and port are 42 | /// mapped to the same external IP address and port. Furthermore, any external host can send 43 | /// a packet to the internal host, by sending a packet to the mapped external address. 44 | FullCone, 45 | 46 | /// A restricted cone NAT is one where all requests from the same internal IP address and 47 | /// port are mapped to the same external IP address and port. Unlike a full cone NAT, an external 48 | /// host (with IP address X) can send a packet to the internal host only if the internal host 49 | /// had previously sent a packet to IP address X. 50 | RestrictedCone, 51 | 52 | /// A port restricted cone NAT is like a restricted cone NAT, but the restriction 53 | /// includes port numbers. Specifically, an external host can send a packet, with source IP 54 | /// address X and source port P, to the internal host only if the internal host had previously 55 | /// sent a packet to IP address X and port P. 56 | PortRestrictedCone, 57 | 58 | /// A symmetric NAT is one where all requests from the same internal IP address and port, 59 | /// to a specific destination IP address and port, are mapped to the same external IP address and 60 | /// port. If the same host sends a packet with the same source address and port, but to 61 | /// a different destination, a different mapping is used. Furthermore, only the external host that 62 | /// receives a packet can send a UDP packet back to the internal host. 63 | Symmetric, 64 | 65 | /// Unknown 66 | Unknown 67 | } 68 | 69 | impl NatType{ 70 | pub fn weight(&self) -> usize{ 71 | match self { 72 | NatType::UdpBlocked => 1, 73 | OpenInternet => 7, 74 | SymmetricUdpFirewall => 2, 75 | FullCone => 6, 76 | RestrictedCone => 5, 77 | PortRestrictedCone => 4, 78 | Symmetric => 3, 79 | NatType::Unknown => 0, 80 | } 81 | } 82 | } 83 | 84 | 85 | /* 86 | In test I, the client sends a STUN Binding Request to a server, without any flags set in the 87 | CHANGE-REQUEST attribute, and without the RESPONSE-ADDRESS attribute. This causes the server 88 | to send the response back to the address and port that the request came from. 89 | 90 | In test II, the client sends a Binding Request with both the "change IP" and "change port" flags 91 | from the CHANGE-REQUEST attribute set. 92 | 93 | In test III, the client sends a Binding Request with only the "change port" flag set. 94 | 95 | +--------+ 96 | | Test | 97 | | I | 98 | +--------+ 99 | | 100 | | 101 | V 102 | /\ /\ 103 | N / \ Y / \ Y +--------+ 104 | UDP <-------/Resp\--------->/ IP \------------->| Test | 105 | Blocked \ ? / \Same/ | II | 106 | \ / \? / +--------+ 107 | \/ \/ | 108 | | N | 109 | | V 110 | V /\ 111 | +--------+ Sym. N / \ 112 | | Test | UDP <---/Resp\ 113 | | II | Firewall \ ? / 114 | +--------+ \ / 115 | | \/ 116 | V |Y 117 | /\ /\ | 118 | Symmetric N / \ +--------+ N / \ V 119 | NAT <--- / IP \<-----| Test |<--- /Resp\ Open 120 | \Same/ | I | \ ? / Internet 121 | \? / +--------+ \ / 122 | \/ \/ 123 | | |Y 124 | | | 125 | | V 126 | | Full 127 | | Cone 128 | V /\ 129 | +--------+ / \ Y 130 | | Test |------>/Resp\---->Restricted 131 | | III | \ ? / 132 | +--------+ \ / 133 | \/ 134 | |N 135 | | Port 136 | +------>Restricted 137 | */ 138 | pub async fn nat_detect_with_servers(stun_server_list: &[&str]) -> IoResult<(NatType, SocketAddr)> { 139 | 140 | 141 | let mut reduce_map: HashMap = HashMap::new(); 142 | let mut handlers = Vec::new(); 143 | 144 | let local_address = local_ip().await?; 145 | debug!("local ip: {}", local_address.ip()); 146 | 147 | for s in stun_server_list { 148 | debug!("{} use", s); 149 | let stun_server = s.to_string(); 150 | let local_address_clone = local_address.clone(); 151 | handlers.push( tokio::spawn(async move { 152 | nat_detect(local_address_clone, &stun_server).await 153 | })); 154 | } 155 | 156 | let mut public_address = empty_address(); 157 | 158 | let empty_address = empty_address(); 159 | 160 | for h in handlers { 161 | let result = h.await.map_err(|_| std::io::Error::from(ErrorKind::Other))?; 162 | if let Result::Ok((a, p,n)) = result { 163 | info!("{} -> {:?}", a, n); 164 | 165 | if !empty_address.eq(&p) { 166 | public_address = p; 167 | } 168 | 169 | reduce_map.entry(n.clone()) 170 | .and_modify(|e| *e += 1) 171 | .or_insert(1); 172 | } 173 | // else if let Result::Err(e) = result{ 174 | // error!("{}", e); 175 | // } 176 | } 177 | 178 | // select maximum count 179 | // if let Option::Some((n, _)) = reduce_map.iter().max_by(|v1, v2| v1.1.cmp(v2.1)){ 180 | // return IoResult::Ok(*n); 181 | // } 182 | 183 | // select maximum weight 184 | if let Option::Some(n) = reduce_map.keys().max_by(|k1, k2| k1.weight().cmp(&k2.weight())){ 185 | return IoResult::Ok((*n, public_address)); 186 | } 187 | 188 | return other_error(); 189 | } 190 | 191 | fn empty_address() -> SocketAddr{ 192 | SocketAddr::new(IpAddr::V4(Ipv4Addr::from(0)), 0) 193 | } 194 | 195 | // #[derive(Debug)] 196 | // pub struct ChangedAddress { 197 | // pub ip: IpAddr, 198 | // pub port: u16, 199 | // } 200 | // 201 | // impl Default for ChangedAddress { 202 | // fn default() -> Self { 203 | // ChangedAddress { 204 | // ip: IpAddr::V4(Ipv4Addr::from(0)), 205 | // port: 0, 206 | // } 207 | // } 208 | // } 209 | // 210 | // impl fmt::Display for ChangedAddress { 211 | // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 212 | // let family = match self.ip { 213 | // IpAddr::V4(_) => FAMILY_IPV4, 214 | // IpAddr::V6(_) => FAMILY_IPV6, 215 | // }; 216 | // if family == FAMILY_IPV4 { 217 | // write!(f, "{}:{}", self.ip, self.port) 218 | // } else { 219 | // write!(f, "[{}]:{}", self.ip, self.port) 220 | // } 221 | // } 222 | // } 223 | 224 | pub async fn local_ip() -> IoResult { 225 | let socket = UdpSocket::bind("0.0.0.0:0").await?; 226 | let _ = socket.connect("8.8.8.8:80").await?; 227 | socket.local_addr() 228 | } 229 | 230 | // impl ChangedAddress { 231 | // /// get_from_as decodes MAPPED-ADDRESS value in message m as an attribute of type t. 232 | // pub fn get_from_as(&mut self, m: &Message, t: AttrType) -> std::result::Result<(), Error> { 233 | // let v = m.get(t)?; 234 | // if v.len() <= 4 { 235 | // return Err(Error::ErrUnexpectedEof); 236 | // } 237 | // 238 | // let family = u16::from_be_bytes([v[0], v[1]]); 239 | // if family != FAMILY_IPV6 && family != FAMILY_IPV4 { 240 | // return Err(Error::Other(format!("bad value {}", family))); 241 | // } 242 | // self.port = u16::from_be_bytes([v[2], v[3]]); 243 | // 244 | // if family == FAMILY_IPV6 { 245 | // let mut ip = [0; IPV6LEN]; 246 | // let l = std::cmp::min(ip.len(), v[4..].len()); 247 | // ip[..l].copy_from_slice(&v[4..4 + l]); 248 | // self.ip = IpAddr::V6(Ipv6Addr::from(ip)); 249 | // } else { 250 | // let mut ip = [0; IPV4LEN]; 251 | // let l = std::cmp::min(ip.len(), v[4..].len()); 252 | // ip[..l].copy_from_slice(&v[4..4 + l]); 253 | // self.ip = IpAddr::V4(Ipv4Addr::from(ip)); 254 | // }; 255 | // 256 | // Ok(()) 257 | // } 258 | // 259 | // /// add_to_as adds MAPPED-ADDRESS value to m as t attribute. 260 | // pub fn add_to_as(&self, m: &mut Message, t: AttrType) -> std::result::Result<(), Error> { 261 | // let family = match self.ip { 262 | // IpAddr::V4(_) => FAMILY_IPV4, 263 | // IpAddr::V6(_) => FAMILY_IPV6, 264 | // }; 265 | // 266 | // let mut value = vec![0u8; 4]; 267 | // //value[0] = 0 // first 8 bits are zeroes 268 | // value[0..2].copy_from_slice(&family.to_be_bytes()); 269 | // value[2..4].copy_from_slice(&self.port.to_be_bytes()); 270 | // 271 | // match self.ip { 272 | // IpAddr::V4(ipv4) => value.extend_from_slice(&ipv4.octets()), 273 | // IpAddr::V6(ipv6) => value.extend_from_slice(&ipv6.octets()), 274 | // }; 275 | // 276 | // m.add(t, &value); 277 | // Ok(()) 278 | // } 279 | // } 280 | 281 | 282 | pub async fn nat_detect(local_address: SocketAddr,stun_server: &str) -> IoResult<(String, SocketAddr, NatType)> { 283 | 284 | let transaction_id = TransactionId::new([3; 12]); 285 | 286 | let mut socket = tokio::net::UdpSocket::bind(format!("{}:0", local_address.ip())).await?; 287 | let mut_socket_ref = &mut socket; 288 | 289 | let stun_server_string = stun_server.to_string(); 290 | 291 | // test1 292 | let test1_message: Message = build_request_bind_message(transaction_id); 293 | debug!("[{}] test1 send: {:?}", stun_server, test1_message); 294 | let result = single_send(stun_server, test1_message, mut_socket_ref).await; 295 | debug!("[{}] test1: {}", stun_server, result.is_ok()); 296 | if result.is_err() { 297 | return IoResult::Ok((stun_server_string, empty_address(),NatType::UdpBlocked)); 298 | } 299 | let test1_response: Message = result.unwrap(); 300 | debug!("[{}] test1 recv: {:?}", stun_server, test1_response); 301 | 302 | let test1_mapped_address = { 303 | let opt: Option<&MappedAddress> = test1_response.get_attribute(); 304 | match opt { 305 | None => return other_error(), 306 | Some(a) => a.address() 307 | } 308 | }; 309 | debug!("[{}] test1 mapped_address: {}", stun_server,test1_mapped_address); 310 | 311 | let public_address = SocketAddr::new(test1_mapped_address.ip().clone(), test1_mapped_address.port().clone()); 312 | 313 | let test1_changed_address ={ 314 | let opt: Option<&ChangedAddress> = test1_response.get_attribute(); 315 | match opt { 316 | None => return other_error(), 317 | Some(a) => a.address() 318 | } 319 | }; 320 | debug!("[{}] test1 changed_address: {}", stun_server,test1_changed_address); 321 | 322 | 323 | // test2 324 | let test2_message = build_request_bind_message_with_attribute( 325 | transaction_id, ChangeRequest::new(true, true) 326 | ); 327 | 328 | let local_ip = mut_socket_ref.local_addr()?.ip(); 329 | let test1_mapped_address_ip = test1_mapped_address.ip(); 330 | let test1_is_same_ip = local_ip.eq(&test1_mapped_address_ip); 331 | debug!("[{}] test1 is_same_ip: l:{} r:{}", stun_server, local_ip, test1_mapped_address_ip); 332 | if test1_is_same_ip { 333 | // no nat 334 | debug!("[{}] test2 send: {:?}", stun_server, test2_message); 335 | let result = single_send::(stun_server, test2_message, mut_socket_ref).await; 336 | debug!("[{}] test2: {}", stun_server, result.is_ok()); 337 | if result.is_err() { 338 | return IoResult::Ok((stun_server_string, public_address,OpenInternet)); 339 | } else { 340 | debug!("[{}] test2 recv: {:?}", stun_server, result.unwrap()); 341 | return IoResult::Ok((stun_server_string, public_address, SymmetricUdpFirewall)); 342 | } 343 | } else { 344 | // nat 345 | debug!("[{}] test2 send: {:?}", stun_server, test2_message); 346 | let result = single_send::(stun_server, test2_message, mut_socket_ref).await; 347 | debug!("[{}] test2: {}", stun_server,result.is_ok()); 348 | if result.is_ok() { 349 | let test2_message = result.unwrap(); 350 | debug!("[{}] test2 recv: {:?}", stun_server, test2_message); 351 | return IoResult::Ok((stun_server_string, public_address, FullCone)); 352 | } else { 353 | // test1(2) 354 | let test1_address = test1_changed_address.to_string(); 355 | 356 | let test12_message: Message = build_request_bind_message(transaction_id); 357 | debug!("[{}] test12 send: {:?}", stun_server,test12_message); 358 | let result = single_send( 359 | test1_address.as_str(), 360 | test12_message, 361 | mut_socket_ref 362 | ).await; 363 | debug!("[{}] test12: {}", stun_server,result.is_ok()); 364 | if result.is_err() { 365 | return IoResult::Ok((stun_server_string,public_address, Unknown)); 366 | } else { 367 | // Symmetric NAT 368 | let test12_response: Message = result.unwrap(); 369 | debug!("[{}] test12 recv: {:?}", stun_server, test12_response); 370 | 371 | let test12_mapped_address ={ 372 | let opt: Option<&MappedAddress> = test12_response.get_attribute(); 373 | match opt { 374 | None => return other_error(), 375 | Some(a) => a.address() 376 | } 377 | }; 378 | debug!("[{}] test12 mapped_address: {}", stun_server,test12_mapped_address); 379 | 380 | if !test1_mapped_address.eq(&test12_mapped_address) { 381 | return IoResult::Ok((stun_server_string, public_address,Symmetric)); 382 | } else { 383 | // test 3 384 | let test3_message = build_request_bind_message_with_attribute( 385 | transaction_id, ChangeRequest::new(false, true) 386 | ); 387 | debug!("[{}] test3 send: {:?}", stun_server, test3_message); 388 | let result = single_send::( 389 | test1_address.as_str(), 390 | test3_message, 391 | mut_socket_ref 392 | ).await; 393 | debug!("[{}] test3: {}", stun_server,result.is_ok()); 394 | if result.is_err() { 395 | return IoResult::Ok((stun_server_string,public_address, PortRestrictedCone)); 396 | } else { 397 | debug!("[{}] test3 recv: {:?}", stun_server, result.unwrap()); 398 | return IoResult::Ok((stun_server_string, public_address,RestrictedCone)); 399 | } 400 | } 401 | } 402 | } 403 | } 404 | 405 | 406 | } 407 | 408 | fn other_error() -> IoResult { 409 | IoResult::Err(std::io::Error::from(ErrorKind::Other)) 410 | } 411 | 412 | fn build_request_bind_message(transaction_id: TransactionId) -> Message { 413 | return Message::new( 414 | MessageClass::Request, 415 | methods::BINDING, 416 | transaction_id 417 | ); 418 | } 419 | 420 | fn build_request_bind_message_with_attribute( 421 | transaction_id: TransactionId, a: A 422 | ) -> Message { 423 | let mut message = build_request_bind_message(transaction_id); 424 | message.add_attribute(a); 425 | message 426 | } 427 | 428 | // pub struct StunResult{ 429 | // request_id: TransactionId, 430 | // bs: Vec 431 | // } 432 | // 433 | // 434 | // impl StunResult { 435 | // fn decode(&self) -> IoResult> { 436 | // todo!() 437 | // } 438 | // } 439 | 440 | async fn single_send( 441 | stun_server: &str, 442 | message: Message, 443 | socket: & mut UdpSocket 444 | ) 445 | -> IoResult> 446 | { 447 | let mut encoder = MessageEncoder::default(); 448 | let bytes: Vec = encoder.encode_into_bytes(message.clone()).map_err(|_e| std::io::Error::from(ErrorKind::Other))?; 449 | let mut buf = [0; 1 << 9]; 450 | for _i in 0..STUN_RETRY_COUNT { 451 | match tokio::time::timeout(TIMEOUT, socket.send_to(bytes.as_slice(), stun_server)).await { 452 | Ok(Ok(_)) => {} 453 | _ => { 454 | continue 455 | } 456 | } 457 | let len = { 458 | match tokio::time::timeout(TIMEOUT, socket.recv_from(&mut buf)) .await { 459 | Ok(Ok((i, _))) => i, 460 | _ => { 461 | continue 462 | } 463 | } 464 | }; 465 | 466 | let mut decoder = MessageDecoder::::new(); 467 | match decoder.decode_from_bytes(&buf[0..len]) { 468 | Ok(Ok(m)) => { 469 | if m.class()== MessageClass::ErrorResponse { 470 | break 471 | } 472 | if message.transaction_id().eq(&m.transaction_id()) { 473 | return IoResult::Ok(m); 474 | } 475 | } 476 | _ => { 477 | continue 478 | } 479 | }; 480 | } 481 | return IoResult::Err(std::io::Error::from(ErrorKind::Other)); 482 | } 483 | 484 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use simple_logger::SimpleLogger; 2 | use nat_detect::nat_detect_with_servers; 3 | use clap::Parser; 4 | use log::LevelFilter; 5 | use rand::Rng; 6 | 7 | #[derive(Parser, Debug)] 8 | #[clap(author, version, about, long_about = None)] 9 | struct Args { 10 | 11 | #[clap(short, long,help="default use https://github.com/pradt2/always-online-stun")] 12 | stun_servers: Option>, 13 | 14 | #[clap(short='c', long, default_value="20")] 15 | stun_servers_count: usize, 16 | 17 | #[clap(short='v', long="verbose")] 18 | verbose: bool, 19 | 20 | } 21 | 22 | #[tokio::main] 23 | pub async fn main(){ 24 | let args: Args = Args::parse(); 25 | let mut logger = SimpleLogger::new(); 26 | if args.verbose { 27 | logger = logger.with_level(LevelFilter::Debug); 28 | } else { 29 | logger = logger.with_level(LevelFilter::Info); 30 | } 31 | logger.init().unwrap(); 32 | let vec = args.stun_servers.unwrap_or_else(|| { 33 | let vec: Vec = include_str!("valid_ipv4s.txt").lines().map(|e|e.trim().to_string()).collect(); 34 | // select 10 server randomly 35 | let mut rng = rand::thread_rng(); 36 | let mut new_vec = Vec::new(); 37 | for _ in 0..args.stun_servers_count { 38 | let stun_server = vec[rng.gen_range(0..vec.len())].to_string(); 39 | new_vec.push(stun_server); 40 | } 41 | new_vec 42 | }); 43 | let stun_servers = vec.iter().map(|e| e.as_str()).collect::>(); 44 | 45 | 46 | match nat_detect_with_servers(stun_servers.as_slice()).await { 47 | Ok(r) => { 48 | println!("{}","#".repeat(32)); 49 | println!(" nat_type: {:?}", r.0); 50 | println!("public_addr: {}", r.1); 51 | } 52 | Err(_) => { 53 | println!("can not detect!"); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/valid_ipv4s.txt: -------------------------------------------------------------------------------- 1 | 217.0.11.225:3478 2 | 217.146.224.39:3478 3 | 34.73.197.25:3478 4 | 195.35.114.37:3478 5 | 176.31.207.161:3478 6 | 217.160.165.10:3478 7 | 128.243.40.39:3478 8 | 178.239.90.248:3478 9 | 5.161.52.174:3478 10 | 106.55.202.232:3478 11 | 185.88.7.40:3478 12 | 195.145.93.141:3478 13 | 139.59.84.212:3478 14 | 111.230.157.11:3478 15 | 217.10.68.145:10000 16 | 154.48.203.210:3478 17 | 70.42.198.30:3478 18 | 212.25.7.87:3478 19 | 89.106.220.34:3478 20 | 212.230.32.233:3478 21 | 54.176.195.118:3478 22 | 154.73.34.8:3478 23 | 217.10.68.145:3478 24 | 66.110.73.74:3478 25 | 111.206.174.2:3478 26 | 195.35.115.37:3478 27 | 195.254.254.20:3478 28 | 91.205.60.185:3478 29 | 27.111.15.93:3478 30 | 92.222.127.114:3478 31 | 134.2.17.14:3478 32 | 95.217.228.176:3478 33 | 74.117.176.114:3478 34 | 45.32.217.190:3478 35 | 46.193.255.81:3478 36 | 80.156.209.102:3478 37 | 45.15.102.31:3478 38 | 106.55.202.194:3478 39 | 150.254.161.60:3478 40 | 69.20.59.115:3478 41 | 83.125.8.47:3478 42 | 217.92.89.8:3478 43 | 37.97.65.52:3478 44 | 88.198.203.20:3478 45 | 209.250.250.224:3478 46 | 212.211.179.42:3478 47 | 188.64.120.28:3478 48 | 82.97.157.254:3478 49 | 182.154.16.7:3478 50 | 185.39.86.17:3478 51 | 84.39.99.22:3478 52 | 108.177.14.127:19305 53 | 18.181.62.206:3478 54 | 115.126.160.126:3478 55 | 185.45.152.22:3478 56 | 84.198.248.217:3478 57 | 198.100.144.121:3478 58 | 194.61.59.30:3478 59 | 176.119.42.11:3478 60 | 34.206.168.53:3478 61 | 136.243.59.79:3478 62 | 77.243.0.75:3478 63 | 62.157.116.5:3478 64 | 74.125.24.127:19302 65 | 213.239.206.5:3478 66 | 130.236.12.148:3478 67 | 49.12.125.53:3478 68 | 91.213.98.56:3478 69 | 83.211.9.232:3478 70 | 217.0.12.1:3478 71 | 177.66.4.31:3478 72 | 104.238.184.174:3478 73 | 83.64.250.246:3478 74 | 217.10.68.152:3478 75 | 198.211.120.59:3478 76 | 185.41.24.6:3478 77 | 37.139.120.14:3478 78 | 91.198.189.39:3478 79 | 34.74.124.204:3478 80 | 209.242.17.106:3478 81 | 54.183.232.212:3478 82 | 74.125.204.127:19302 83 | 94.140.180.141:3478 84 | 54.173.127.164:3478 85 | 216.228.192.76:3478 86 | 212.144.246.197:3478 87 | 106.55.201.252:3478 88 | 195.242.206.1:3478 89 | 23.21.199.62:3478 90 | 159.69.191.124:3478 91 | 193.22.17.97:3478 92 | 77.72.169.213:3478 93 | 31.184.236.23:3478 94 | 81.25.228.2:3478 95 | 5.178.34.84:3478 96 | 217.19.174.42:3478 97 | 212.101.4.120:3478 98 | 154.48.203.211:3478 99 | 213.140.209.236:3478 100 | 106.55.201.94:3478 101 | 217.0.12.17:3478 102 | 188.138.90.169:3478 103 | 18.141.235.117:3478 104 | 172.217.213.127:19305 105 | 138.201.70.161:3478 106 | 188.123.97.201:3478 107 | 65.99.199.231:3478 108 | 34.193.110.91:3478 109 | 79.174.66.51:3478 110 | 44.230.252.214:3478 111 | 185.61.119.19:3478 112 | 172.105.99.33:3478 113 | 91.151.52.200:3478 114 | 13.59.93.103:3478 115 | 13.228.116.50:3478 116 | 104.130.214.5:3478 117 | 91.217.201.14:3478 118 | 124.64.206.224:8800 119 | 94.23.17.185:3478 120 | 212.103.68.7:3478 121 | 88.218.220.40:3478 122 | 111.206.174.3:3478 123 | 217.91.243.229:3478 124 | 185.67.224.58:3478 125 | 90.145.158.66:3478 126 | 212.45.38.40:3478 127 | 212.80.226.18:3478 128 | 212.227.67.34:3478 129 | 80.250.1.173:3478 130 | 88.99.67.241:3478 131 | 41.79.23.6:3478 132 | 216.190.232.121:3478 133 | 81.3.27.44:3478 134 | 95.110.198.3:3478 135 | 143.198.60.79:3478 136 | 91.121.128.132:3478 137 | 42.121.15.99:3478 138 | 131.153.146.5:3478 139 | 178.77.74.88:3478 140 | 91.199.161.149:3478 141 | 141.54.160.48:3478 142 | 142.250.21.127:19305 143 | 212.29.18.56:3478 144 | 157.161.10.32:3478 145 | 193.22.2.248:3478 146 | 52.24.174.49:3478 147 | 192.99.194.90:3478 148 | 180.235.108.91:3478 149 | 194.149.72.133:3478 150 | 54.36.67.77:3478 151 | 91.121.209.194:3478 152 | 194.87.0.22:3478 153 | 142.250.21.127:19302 154 | 51.83.201.84:3478 155 | 87.129.12.229:3478 156 | 193.22.119.20:3478 157 | 146.185.186.157:3478 158 | 18.191.223.12:3478 159 | 217.0.11.241:3478 160 | 195.211.238.18:3478 161 | 85.17.186.7:3478 162 | 207.38.89.164:3478 163 | 217.19.174.41:3478 164 | 106.55.200.13:3478 165 | 213.239.212.105:3478 166 | 192.172.233.145:3478 167 | 51.68.112.203:3478 168 | 185.18.24.50:3478 169 | 213.251.48.147:3478 170 | 161.53.1.100:3478 171 | 88.198.151.128:3478 172 | 185.140.24.140:3478 173 | 79.140.42.88:3478 174 | 31.13.72.3:3478 175 | 37.9.136.90:3478 176 | 94.130.130.49:3478 177 | 65.17.128.101:3478 178 | 52.71.78.21:3478 179 | 217.119.210.45:3478 180 | 95.56.227.227:3478 181 | 34.192.137.246:3478 182 | 108.177.14.127:19302 183 | 37.157.193.46:3478 184 | 95.216.78.222:3478 185 | 62.138.0.157:3478 186 | 23.252.81.20:3478 187 | 212.18.0.14:3478 188 | 173.255.213.166:3478 189 | 173.255.200.200:3478 190 | 109.69.177.38:3478 191 | 217.10.68.152:10000 192 | 193.28.184.4:3478 193 | 194.61.59.25:3478 194 | 51.83.15.212:3478 195 | 217.74.179.29:3478 196 | 158.69.57.20:3478 197 | 130.244.125.91:3478 198 | 202.49.164.49:3478 199 | 18.188.120.144:3478 200 | 81.23.228.150:3478 201 | 109.235.234.65:3478 202 | 81.19.224.87:3478 203 | 66.228.54.23:3478 204 | 203.20.110.2:3478 205 | 77.72.169.211:3478 206 | 185.112.247.26:3478 207 | 185.125.180.70:3478 208 | 109.68.96.189:3478 209 | 217.0.136.17:3478 210 | 52.47.70.236:3478 211 | 81.173.115.217:3478 212 | 178.33.166.29:3478 213 | 213.136.74.244:3478 214 | 137.74.112.113:3478 215 | 95.163.107.194:3478 216 | 149.56.14.164:3478 217 | 54.177.85.190:3478 218 | 198.72.119.88:3478 219 | 204.197.159.2:3478 220 | 172.217.213.127:19302 221 | 195.208.107.138:3478 222 | 194.169.214.30:3478 223 | 134.119.17.210:3478 224 | 178.63.240.148:3478 225 | 106.55.200.17:3478 226 | 66.51.128.11:3478 227 | 147.182.188.245:3478 228 | 129.146.30.27:3478 229 | 80.155.54.123:3478 230 | 208.83.61.2:3478 231 | 83.96.215.63:3478 232 | 13.250.110.196:3478 233 | 188.118.52.172:3478 234 | 198.61.197.182:3478 235 | 63.211.239.133:3478 236 | 157.245.15.73:3478 237 | 192.76.120.66:3478 238 | 182.154.16.5:3478 239 | 195.209.116.72:3478 240 | 94.75.247.45:3478 241 | 213.157.4.53:3478 242 | 108.171.179.113:3478 243 | 77.246.121.149:3478 244 | 95.216.145.84:3478 245 | 81.82.206.117:3478 246 | 104.131.174.103:3478 247 | 209.105.241.31:3478 248 | 81.83.12.46:3478 249 | 188.68.43.182:3478 250 | 212.103.200.6:3478 251 | 104.130.210.14:3478 252 | 5.9.87.18:3478 253 | 217.19.216.178:3478 254 | 54.223.41.250:3478 255 | 37.59.92.57:3478 256 | 77.237.51.83:3478 257 | 54.197.117.0:3478 258 | 185.158.144.41:3478 259 | 176.9.24.184:3478 260 | 193.43.148.37:3478 261 | 77.246.29.197:3478 262 | 52.26.251.34:3478 263 | 199.4.110.11:3478 264 | 92.63.111.151:3478 265 | 158.69.221.198:3478 266 | 162.243.29.166:3478 267 | 216.144.89.2:3478 268 | 203.13.68.16:3478 269 | 185.88.236.76:3478 270 | 70.42.198.34:3478 271 | 82.113.193.63:3478 272 | 5.9.112.14:3478 273 | 62.96.96.137:3478 274 | 51.68.45.75:3478 275 | 212.69.48.253:3478 276 | 5.161.57.75:3478 277 | 77.72.169.212:3478 278 | 217.0.136.1:3478 279 | 195.201.132.113:3478 280 | 78.111.72.53:3478 281 | 159.69.191.124:443 282 | 87.253.140.133:3478 283 | 78.111.79.151:3478 284 | 85.17.88.164:3478 285 | 78.40.125.40:3478 286 | 202.49.164.50:3478 287 | 44.224.155.217:3478 288 | 88.86.102.51:3478 289 | 91.121.31.8:3478 290 | 212.53.40.40:3478 291 | 212.227.67.33:3478 292 | 18.141.157.182:3478 293 | 194.140.246.192:3478 294 | 157.22.130.80:3478 295 | 52.76.91.67:3478 296 | 82.135.97.42:3478 297 | 89.185.235.201:3478 298 | 74.125.24.127:19305 299 | 185.41.24.10:3478 300 | 176.9.179.80:3478 301 | 85.197.87.182:3478 302 | 136.243.202.77:3478 303 | 185.67.224.59:3478 304 | 77.72.169.210:3478 305 | 3.208.102.142:3478 306 | 176.62.31.10:3478 307 | 193.16.148.245:3478 308 | 54.72.235.205:3478 309 | 23.253.102.137:3478 310 | 74.125.204.127:19305 311 | 81.91.111.11:3478 312 | 178.63.107.149:3478 313 | 121.101.91.194:3478 314 | 91.122.224.102:3478 315 | 69.89.160.30:3478 316 | 52.52.70.85:3478 317 | 85.93.219.114:3478 318 | 81.162.64.162:3478 319 | 162.13.119.185:3478 320 | 204.197.144.2:3478 --------------------------------------------------------------------------------