mirror of https://github.com/telemt/telemt.git
feat: add hostname support for SOCKS4/SOCKS5 upstream proxies
Previously, SOCKS proxy addresses only accepted IP:port format. Now both IP:port and hostname:port formats are supported. Changes: - Try parsing as SocketAddr first (IP:port) for backward compatibility - Fall back to tokio::net::TcpStream::connect() for hostname resolution - Log warning if interface binding is specified with hostname (not supported) Example usage: [[upstreams]] type = "socks5" address = "proxy.example.com:1080" username = "user" password = "pass"
This commit is contained in:
parent
7da062e448
commit
100cb92ad1
|
|
@ -383,32 +383,43 @@ impl UpstreamManager {
|
||||||
Ok(stream)
|
Ok(stream)
|
||||||
},
|
},
|
||||||
UpstreamType::Socks4 { address, interface, user_id } => {
|
UpstreamType::Socks4 { address, interface, user_id } => {
|
||||||
let proxy_addr: SocketAddr = address.parse()
|
// Try to parse as SocketAddr first (IP:port), otherwise treat as hostname:port
|
||||||
.map_err(|_| ProxyError::Config("Invalid SOCKS4 address".to_string()))?;
|
let mut stream = if let Ok(proxy_addr) = address.parse::<SocketAddr>() {
|
||||||
|
// IP:port format - use socket with optional interface binding
|
||||||
|
let bind_ip = Self::resolve_bind_address(
|
||||||
|
interface,
|
||||||
|
&None,
|
||||||
|
proxy_addr,
|
||||||
|
bind_rr.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
let bind_ip = Self::resolve_bind_address(
|
let socket = create_outgoing_socket_bound(proxy_addr, bind_ip)?;
|
||||||
interface,
|
|
||||||
&None,
|
|
||||||
proxy_addr,
|
|
||||||
bind_rr.as_deref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let socket = create_outgoing_socket_bound(proxy_addr, bind_ip)?;
|
socket.set_nonblocking(true)?;
|
||||||
|
match socket.connect(&proxy_addr.into()) {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) || err.kind() == std::io::ErrorKind::WouldBlock => {},
|
||||||
|
Err(err) => return Err(ProxyError::Io(err)),
|
||||||
|
}
|
||||||
|
|
||||||
socket.set_nonblocking(true)?;
|
let std_stream: std::net::TcpStream = socket.into();
|
||||||
match socket.connect(&proxy_addr.into()) {
|
let stream = TcpStream::from_std(std_stream)?;
|
||||||
Ok(()) => {},
|
|
||||||
Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) || err.kind() == std::io::ErrorKind::WouldBlock => {},
|
|
||||||
Err(err) => return Err(ProxyError::Io(err)),
|
|
||||||
}
|
|
||||||
|
|
||||||
let std_stream: std::net::TcpStream = socket.into();
|
stream.writable().await?;
|
||||||
let mut stream = TcpStream::from_std(std_stream)?;
|
if let Some(e) = stream.take_error()? {
|
||||||
|
return Err(ProxyError::Io(e));
|
||||||
|
}
|
||||||
|
stream
|
||||||
|
} else {
|
||||||
|
// Hostname:port format - use tokio DNS resolution
|
||||||
|
// Note: interface binding is not supported for hostnames
|
||||||
|
if interface.is_some() {
|
||||||
|
warn!("SOCKS4 interface binding is not supported for hostname addresses, ignoring");
|
||||||
|
}
|
||||||
|
TcpStream::connect(address).await
|
||||||
|
.map_err(ProxyError::Io)?
|
||||||
|
};
|
||||||
|
|
||||||
stream.writable().await?;
|
|
||||||
if let Some(e) = stream.take_error()? {
|
|
||||||
return Err(ProxyError::Io(e));
|
|
||||||
}
|
|
||||||
// replace socks user_id with config.selected_scope, if set
|
// replace socks user_id with config.selected_scope, if set
|
||||||
let scope: Option<&str> = Some(config.selected_scope.as_str())
|
let scope: Option<&str> = Some(config.selected_scope.as_str())
|
||||||
.filter(|s| !s.is_empty());
|
.filter(|s| !s.is_empty());
|
||||||
|
|
@ -418,32 +429,42 @@ impl UpstreamManager {
|
||||||
Ok(stream)
|
Ok(stream)
|
||||||
},
|
},
|
||||||
UpstreamType::Socks5 { address, interface, username, password } => {
|
UpstreamType::Socks5 { address, interface, username, password } => {
|
||||||
let proxy_addr: SocketAddr = address.parse()
|
// Try to parse as SocketAddr first (IP:port), otherwise treat as hostname:port
|
||||||
.map_err(|_| ProxyError::Config("Invalid SOCKS5 address".to_string()))?;
|
let mut stream = if let Ok(proxy_addr) = address.parse::<SocketAddr>() {
|
||||||
|
// IP:port format - use socket with optional interface binding
|
||||||
|
let bind_ip = Self::resolve_bind_address(
|
||||||
|
interface,
|
||||||
|
&None,
|
||||||
|
proxy_addr,
|
||||||
|
bind_rr.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
let bind_ip = Self::resolve_bind_address(
|
let socket = create_outgoing_socket_bound(proxy_addr, bind_ip)?;
|
||||||
interface,
|
|
||||||
&None,
|
|
||||||
proxy_addr,
|
|
||||||
bind_rr.as_deref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let socket = create_outgoing_socket_bound(proxy_addr, bind_ip)?;
|
socket.set_nonblocking(true)?;
|
||||||
|
match socket.connect(&proxy_addr.into()) {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) || err.kind() == std::io::ErrorKind::WouldBlock => {},
|
||||||
|
Err(err) => return Err(ProxyError::Io(err)),
|
||||||
|
}
|
||||||
|
|
||||||
socket.set_nonblocking(true)?;
|
let std_stream: std::net::TcpStream = socket.into();
|
||||||
match socket.connect(&proxy_addr.into()) {
|
let stream = TcpStream::from_std(std_stream)?;
|
||||||
Ok(()) => {},
|
|
||||||
Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) || err.kind() == std::io::ErrorKind::WouldBlock => {},
|
|
||||||
Err(err) => return Err(ProxyError::Io(err)),
|
|
||||||
}
|
|
||||||
|
|
||||||
let std_stream: std::net::TcpStream = socket.into();
|
stream.writable().await?;
|
||||||
let mut stream = TcpStream::from_std(std_stream)?;
|
if let Some(e) = stream.take_error()? {
|
||||||
|
return Err(ProxyError::Io(e));
|
||||||
stream.writable().await?;
|
}
|
||||||
if let Some(e) = stream.take_error()? {
|
stream
|
||||||
return Err(ProxyError::Io(e));
|
} else {
|
||||||
}
|
// Hostname:port format - use tokio DNS resolution
|
||||||
|
// Note: interface binding is not supported for hostnames
|
||||||
|
if interface.is_some() {
|
||||||
|
warn!("SOCKS5 interface binding is not supported for hostname addresses, ignoring");
|
||||||
|
}
|
||||||
|
TcpStream::connect(address).await
|
||||||
|
.map_err(ProxyError::Io)?
|
||||||
|
};
|
||||||
|
|
||||||
debug!(config = ?config, "Socks5 connection");
|
debug!(config = ?config, "Socks5 connection");
|
||||||
// replace socks user:pass with config.selected_scope, if set
|
// replace socks user:pass with config.selected_scope, if set
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue