ipv6 support for direct connection, todo: UI input check, relay port
change based on ipv6
This commit is contained in:
parent
fec4e3a049
commit
55962f2fc9
@ -285,7 +285,7 @@ mod tests {
|
||||
|
||||
let addr = "[2001:db8::1]:8080".parse::<SocketAddr>().unwrap();
|
||||
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
|
||||
|
||||
|
||||
let addr = "[2001:db8:ff::1111]:80".parse::<SocketAddr>().unwrap();
|
||||
assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
|
||||
}
|
||||
@ -306,4 +306,37 @@ pub fn is_ipv4_str(id: &str) -> bool {
|
||||
regex::Regex::new(r"^\d+\.\d+\.\d+\.\d+(:\d+)?$")
|
||||
.unwrap()
|
||||
.is_match(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_ipv6_str(id: &str) -> bool {
|
||||
regex::Regex::new(r"^((([a-fA-F0-9]{1,4}:{1,2})+[a-fA-F0-9]{1,4})|(\[([a-fA-F0-9]{1,4}:{1,2})+[a-fA-F0-9]{1,4}\]:\d+))$")
|
||||
.unwrap()
|
||||
.is_match(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_ip_str(id: &str) -> bool {
|
||||
is_ipv4_str(id) || is_ipv6_str(id)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_lib {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_ipv6() {
|
||||
assert_eq!(is_ipv6_str("1:2:3"), true);
|
||||
assert_eq!(is_ipv6_str("[ab:2:3]:12"), true);
|
||||
assert_eq!(is_ipv6_str("[ABEF:2a:3]:12"), true);
|
||||
assert_eq!(is_ipv6_str("[ABEG:2a:3]:12"), false);
|
||||
assert_eq!(is_ipv6_str("1[ab:2:3]:12"), false);
|
||||
assert_eq!(is_ipv6_str("1.1.1.1"), false);
|
||||
assert_eq!(is_ip_str("1.1.1.1"), true);
|
||||
assert_eq!(is_ipv6_str("1:2:"), false);
|
||||
assert_eq!(is_ipv6_str("1:2::0"), true);
|
||||
assert_eq!(is_ipv6_str("[1:2::0]:1"), true);
|
||||
assert_eq!(is_ipv6_str("[1:2::0]:"), false);
|
||||
assert_eq!(is_ipv6_str("1:2::0]:1"), false);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use protobuf::Message;
|
||||
use sodiumoxide::crypto::secretbox::{self, Key, Nonce};
|
||||
use std::{
|
||||
io::{self, Error, ErrorKind},
|
||||
net::SocketAddr,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
@ -258,6 +258,38 @@ pub async fn new_listener<T: ToSocketAddrs>(addr: T, reuse: bool) -> ResultType<
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn listen_any(port: u16) -> ResultType<TcpListener> {
|
||||
if let Ok(mut socket) = TcpSocket::new_v6() {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::io::{FromRawFd, IntoRawFd};
|
||||
let raw_fd = socket.into_raw_fd();
|
||||
let sock2 = unsafe { socket2::Socket::from_raw_fd(raw_fd) };
|
||||
sock2.set_only_v6(false).ok();
|
||||
socket = unsafe { TcpSocket::from_raw_fd(sock2.into_raw_fd()) };
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::prelude::{FromRawSocket, IntoRawSocket};
|
||||
let raw_socket = socket.into_raw_socket();
|
||||
let sock2 = unsafe { socket2::Socket::from_raw_socket(raw_socket) };
|
||||
sock2.set_only_v6(false).ok();
|
||||
socket = unsafe { TcpSocket::from_raw_socket(sock2.into_raw_socket()) };
|
||||
}
|
||||
if socket
|
||||
.bind(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port))
|
||||
.is_ok()
|
||||
{
|
||||
if let Ok(l) = socket.listen(DEFAULT_BACKLOG) {
|
||||
return Ok(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
let s = TcpSocket::new_v4()?;
|
||||
s.bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port))?;
|
||||
Ok(s.listen(DEFAULT_BACKLOG)?)
|
||||
}
|
||||
|
||||
impl Unpin for DynTcpStream {}
|
||||
|
||||
impl AsyncRead for DynTcpStream {
|
||||
|
@ -167,7 +167,7 @@ impl Client {
|
||||
interface: impl Interface,
|
||||
) -> ResultType<(Stream, bool)> {
|
||||
// to-do: remember the port for each peer, so that we can retry easier
|
||||
if hbb_common::is_ipv4_str(peer) {
|
||||
if hbb_common::is_ip_str(peer) {
|
||||
return Ok((
|
||||
socket_client::connect_tcp(
|
||||
crate::check_port(peer, RELAY_PORT + 1),
|
||||
@ -376,7 +376,8 @@ impl Client {
|
||||
log::info!("peer address: {}, timeout: {}", peer, connect_timeout);
|
||||
let start = std::time::Instant::now();
|
||||
// NOTICE: Socks5 is be used event in intranet. Which may be not a good way.
|
||||
let mut conn = socket_client::connect_tcp_local(peer, Some(local_addr), connect_timeout).await;
|
||||
let mut conn =
|
||||
socket_client::connect_tcp_local(peer, Some(local_addr), connect_timeout).await;
|
||||
let mut direct = !conn.is_err();
|
||||
if interface.is_force_relay() || conn.is_err() {
|
||||
if !relay_server.is_empty() {
|
||||
@ -1847,7 +1848,10 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
||||
|
||||
fn get_login_config_handler(&self) -> Arc<RwLock<LoginConfigHandler>>;
|
||||
fn set_force_relay(&self, direct: bool, received: bool) {
|
||||
self.get_login_config_handler().write().unwrap().set_force_relay(direct, received);
|
||||
self.get_login_config_handler()
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_force_relay(direct, received);
|
||||
}
|
||||
fn is_force_relay(&self) -> bool {
|
||||
self.get_login_config_handler().read().unwrap().force_relay
|
||||
|
@ -21,7 +21,7 @@ use hbb_common::{
|
||||
anyhow::bail,
|
||||
compress::compress as compress_func,
|
||||
config::{self, Config, COMPRESS_LEVEL, RENDEZVOUS_TIMEOUT},
|
||||
get_version_number, log,
|
||||
get_version_number, is_ipv6_str, log,
|
||||
message_proto::*,
|
||||
protobuf::Enum,
|
||||
protobuf::Message as _,
|
||||
@ -477,6 +477,12 @@ pub fn username() -> String {
|
||||
#[inline]
|
||||
pub fn check_port<T: std::string::ToString>(host: T, port: i32) -> String {
|
||||
let host = host.to_string();
|
||||
if is_ipv6_str(&host) {
|
||||
if host.contains("[") {
|
||||
return host;
|
||||
}
|
||||
return format!("[{}]:{}", host, port);
|
||||
}
|
||||
if !host.contains(":") {
|
||||
return format!("{}:{}", host, port);
|
||||
}
|
||||
@ -706,3 +712,17 @@ pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> Strin
|
||||
fd_json.insert("entries".into(), json!(entries_out));
|
||||
serde_json::to_string(&fd_json).unwrap_or("".into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_common {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_check_port() {
|
||||
assert_eq!(check_port("[1:2]:12", 32), "[1:2]:12");
|
||||
assert_eq!(check_port("1:2", 32), "[1:2]:32");
|
||||
assert_eq!(check_port("1.1.1.1", 32), "1.1.1.1:32");
|
||||
assert_eq!(check_port("1.1.1.1:32", 32), "1.1.1.1:32");
|
||||
assert_eq!(check_port("test.com:32", 0), "test.com:32");
|
||||
}
|
||||
}
|
||||
|
@ -506,8 +506,7 @@ async fn direct_server(server: ServerPtr) {
|
||||
let disabled = Config::get_option("direct-server").is_empty();
|
||||
if !disabled && listener.is_none() {
|
||||
port = get_direct_port();
|
||||
let addr = format!("0.0.0.0:{}", port);
|
||||
match hbb_common::tcp::new_listener(&addr, false).await {
|
||||
match hbb_common::tcp::listen_any(port as _).await {
|
||||
Ok(l) => {
|
||||
listener = Some(l);
|
||||
log::info!(
|
||||
@ -518,8 +517,8 @@ async fn direct_server(server: ServerPtr) {
|
||||
Err(err) => {
|
||||
// to-do: pass to ui
|
||||
log::error!(
|
||||
"Failed to start direct server on : {}, error: {}",
|
||||
addr,
|
||||
"Failed to start direct server on port: {}, error: {}",
|
||||
port,
|
||||
err
|
||||
);
|
||||
loop {
|
||||
|
Loading…
x
Reference in New Issue
Block a user