|
|
|
@@ -25,6 +25,9 @@ struct BindRequestResponse {
|
|
|
|
|
|
|
|
|
|
ip_changed: bool,
|
|
|
|
|
port_changed: bool,
|
|
|
|
|
|
|
|
|
|
real_ip_changed: bool,
|
|
|
|
|
real_port_changed: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BindRequestResponse {
|
|
|
|
@@ -42,11 +45,15 @@ impl Stun {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(skip(self, buf))]
|
|
|
|
|
async fn wait_stun_response<'a, const N: usize>(
|
|
|
|
|
&self,
|
|
|
|
|
buf: &'a mut [u8; N],
|
|
|
|
|
udp: &UdpSocket,
|
|
|
|
|
tids: &Vec<u128>,
|
|
|
|
|
expected_ip_changed: bool,
|
|
|
|
|
expected_port_changed: bool,
|
|
|
|
|
stun_host: &SocketAddr,
|
|
|
|
|
) -> Result<(stun_format::Msg<'a>, SocketAddr), Error> {
|
|
|
|
|
let mut now = tokio::time::Instant::now();
|
|
|
|
|
let deadline = now + self.resp_timeout;
|
|
|
|
@@ -66,19 +73,33 @@ impl Stun {
|
|
|
|
|
unsafe { std::ptr::copy(udp_buf.as_ptr(), buf.as_ptr() as *mut u8, len) };
|
|
|
|
|
|
|
|
|
|
let msg = stun_format::Msg::<'a>::from(&buf[..]);
|
|
|
|
|
tracing::trace!(b = ?&udp_buf[..len], ?msg, ?tids, "recv stun response");
|
|
|
|
|
tracing::trace!(b = ?&udp_buf[..len], ?msg, ?tids, ?remote_addr, "recv stun response");
|
|
|
|
|
|
|
|
|
|
if msg.typ().is_none() || msg.tid().is_none() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if matches!(
|
|
|
|
|
if !matches!(
|
|
|
|
|
msg.typ().as_ref().unwrap(),
|
|
|
|
|
stun_format::MsgType::BindingResponse
|
|
|
|
|
) && tids.contains(msg.tid().as_ref().unwrap())
|
|
|
|
|
) || !tids.contains(msg.tid().as_ref().unwrap())
|
|
|
|
|
{
|
|
|
|
|
return Ok((msg, remote_addr));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// some stun server use changed socket even we don't ask for.
|
|
|
|
|
if expected_ip_changed && stun_host.ip() == remote_addr.ip() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if expected_port_changed
|
|
|
|
|
&& stun_host.ip() == remote_addr.ip()
|
|
|
|
|
&& stun_host.port() == remote_addr.port()
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ok((msg, remote_addr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Err(Error::Unknown)
|
|
|
|
@@ -140,16 +161,6 @@ impl Stun {
|
|
|
|
|
.await?
|
|
|
|
|
.next()
|
|
|
|
|
.ok_or(Error::NotFound)?;
|
|
|
|
|
// let udp_socket = socket2::Socket::new(
|
|
|
|
|
// match stun_host {
|
|
|
|
|
// SocketAddr::V4(..) => socket2::Domain::IPV4,
|
|
|
|
|
// SocketAddr::V6(..) => socket2::Domain::IPV6,
|
|
|
|
|
// },
|
|
|
|
|
// socket2::Type::DGRAM,
|
|
|
|
|
// Some(socket2::Protocol::UDP),
|
|
|
|
|
// )?;
|
|
|
|
|
// udp_socket.set_reuse_port(true)?;
|
|
|
|
|
// udp_socket.set_reuse_address(true)?;
|
|
|
|
|
let udp = UdpSocket::bind(format!("0.0.0.0:{}", source_port)).await?;
|
|
|
|
|
|
|
|
|
|
// repeat req in case of packet loss
|
|
|
|
@@ -177,18 +188,23 @@ impl Stun {
|
|
|
|
|
|
|
|
|
|
tracing::trace!("waiting stun response");
|
|
|
|
|
let mut buf = [0; 1620];
|
|
|
|
|
let (msg, recv_addr) = self.wait_stun_response(&mut buf, &udp, &tids).await?;
|
|
|
|
|
let (msg, recv_addr) = self
|
|
|
|
|
.wait_stun_response(&mut buf, &udp, &tids, change_ip, change_port, &stun_host)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
let changed_socket_addr = Self::extract_changed_addr(&msg);
|
|
|
|
|
let ip_changed = stun_host.ip() != recv_addr.ip();
|
|
|
|
|
let port_changed = stun_host.port() != recv_addr.port();
|
|
|
|
|
let real_ip_changed = stun_host.ip() != recv_addr.ip();
|
|
|
|
|
let real_port_changed = stun_host.port() != recv_addr.port();
|
|
|
|
|
|
|
|
|
|
let resp = BindRequestResponse {
|
|
|
|
|
source_addr: udp.local_addr()?,
|
|
|
|
|
mapped_socket_addr: Self::extrace_mapped_addr(&msg),
|
|
|
|
|
changed_socket_addr,
|
|
|
|
|
ip_changed,
|
|
|
|
|
port_changed,
|
|
|
|
|
ip_changed: change_ip,
|
|
|
|
|
port_changed: change_port,
|
|
|
|
|
|
|
|
|
|
real_ip_changed,
|
|
|
|
|
real_port_changed,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tracing::info!(
|
|
|
|
@@ -396,24 +412,25 @@ mod tests {
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn test_stun_bind_request() {
|
|
|
|
|
// miwifi / qq seems not correctly responde to change_ip and change_port, they always try to change the src ip and port.
|
|
|
|
|
// let stun = Stun::new("stun.counterpath.com:3478".to_string());
|
|
|
|
|
let stun = Stun::new("180.235.108.91:3478".to_string());
|
|
|
|
|
let stun = Stun::new("stun1.l.google.com:19302".to_string());
|
|
|
|
|
// let stun = Stun::new("180.235.108.91:3478".to_string());
|
|
|
|
|
// let stun = Stun::new("193.22.2.248:3478".to_string());
|
|
|
|
|
|
|
|
|
|
// let stun = Stun::new("stun.chat.bilibili.com:3478".to_string());
|
|
|
|
|
// let stun = Stun::new("stun.miwifi.com:3478".to_string());
|
|
|
|
|
|
|
|
|
|
let rs = stun.bind_request(12345, true, true).await.unwrap();
|
|
|
|
|
assert!(rs.ip_changed);
|
|
|
|
|
assert!(rs.port_changed);
|
|
|
|
|
// github actions are port restricted nat, so we only test last one.
|
|
|
|
|
|
|
|
|
|
let rs = stun.bind_request(12345, true, false).await.unwrap();
|
|
|
|
|
assert!(rs.ip_changed);
|
|
|
|
|
assert!(!rs.port_changed);
|
|
|
|
|
// let rs = stun.bind_request(12345, true, true).await.unwrap();
|
|
|
|
|
// assert!(rs.ip_changed);
|
|
|
|
|
// assert!(rs.port_changed);
|
|
|
|
|
|
|
|
|
|
let rs = stun.bind_request(12345, false, true).await.unwrap();
|
|
|
|
|
assert!(!rs.ip_changed);
|
|
|
|
|
assert!(rs.port_changed);
|
|
|
|
|
// let rs = stun.bind_request(12345, true, false).await.unwrap();
|
|
|
|
|
// assert!(rs.ip_changed);
|
|
|
|
|
// assert!(!rs.port_changed);
|
|
|
|
|
|
|
|
|
|
// let rs = stun.bind_request(12345, false, true).await.unwrap();
|
|
|
|
|
// assert!(!rs.ip_changed);
|
|
|
|
|
// assert!(rs.port_changed);
|
|
|
|
|
|
|
|
|
|
let rs = stun.bind_request(12345, false, false).await.unwrap();
|
|
|
|
|
assert!(!rs.ip_changed);
|
|
|
|
@@ -428,6 +445,6 @@ mod tests {
|
|
|
|
|
]);
|
|
|
|
|
let ret = detector.get_udp_nat_type(0).await;
|
|
|
|
|
|
|
|
|
|
assert_eq!(ret, NatType::FullCone);
|
|
|
|
|
assert_ne!(ret, NatType::Unknown);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|