mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-02-22 09:38:32 +08:00
Compare commits
7 Commits
display_na
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25719ecd0d | ||
|
|
5d490b2bdd | ||
|
|
7de2390d23 | ||
|
|
16dde51e23 | ||
|
|
780f99bd32 | ||
|
|
1b87c33fdc | ||
|
|
ef59778720 |
109
docs/NETWORK_BINDING.md
Normal file
109
docs/NETWORK_BINDING.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Network Interface Binding
|
||||
|
||||
RustDesk can be configured to bind to a specific network interface by IP address.
|
||||
|
||||
## Configuration
|
||||
|
||||
To bind RustDesk to a specific network interface, set the `bind-interface` option in your configuration.
|
||||
|
||||
### Option Name
|
||||
`bind-interface`
|
||||
|
||||
### Supported Values
|
||||
- Empty string (default): Bind to all available interfaces (0.0.0.0 for IPv4, :: for IPv6)
|
||||
- IPv4 address: e.g., `192.168.1.100`, `10.0.0.1`
|
||||
- IPv6 address: e.g., `::1`, `fe80::1`, `2001:db8::1`
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Bind to a specific IPv4 address
|
||||
To bind RustDesk to only listen on interface with IP address `192.168.1.100`:
|
||||
|
||||
```json
|
||||
{
|
||||
"options": {
|
||||
"bind-interface": "192.168.1.100"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Bind to IPv6 localhost
|
||||
```json
|
||||
{
|
||||
"options": {
|
||||
"bind-interface": "::1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Bind to all interfaces (default)
|
||||
```json
|
||||
{
|
||||
"options": {
|
||||
"bind-interface": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or simply omit the `bind-interface` option entirely.
|
||||
|
||||
## How It Works
|
||||
|
||||
When the `bind-interface` option is set:
|
||||
|
||||
1. RustDesk reads the configuration when starting the direct server
|
||||
2. If `bind-interface` is empty or not set, it binds to all available network interfaces
|
||||
3. If `bind-interface` contains a valid IP address:
|
||||
- RustDesk validates the IP address format (supports both IPv4 and IPv6)
|
||||
- Creates a TCP socket and binds it to the specified IP address
|
||||
- Starts listening for connections on that interface only
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Multiple Network Interfaces
|
||||
If your machine has multiple network interfaces (e.g., Ethernet, Wi-Fi, VPN), you can force RustDesk to use a specific one:
|
||||
|
||||
- **Example**: Force RustDesk to use the Ethernet interface at `192.168.1.100` instead of the Wi-Fi interface at `192.168.2.50`
|
||||
|
||||
### Security
|
||||
Restrict RustDesk to listen only on internal network interfaces:
|
||||
|
||||
- **Example**: Bind to `10.0.0.5` (internal network) instead of listening on all interfaces including public-facing ones
|
||||
|
||||
### VPN/Tunneling
|
||||
Force RustDesk to use a VPN or tunnel interface:
|
||||
|
||||
- **Example**: Bind to the VPN interface IP address to ensure all traffic goes through the VPN
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Invalid bind address error
|
||||
If you see an error like "Invalid bind interface address", check that:
|
||||
- The IP address format is correct (no typos)
|
||||
- The IP address is valid (e.g., not `999.999.999.999`)
|
||||
- The IP address exists on one of your machine's network interfaces
|
||||
|
||||
### Failed to start direct server
|
||||
If RustDesk fails to start with a bind error, it could be because:
|
||||
- The specified IP address doesn't exist on your machine
|
||||
- Another application is already using the port on that interface
|
||||
- You don't have permission to bind to that address
|
||||
|
||||
### Finding your network interface IP addresses
|
||||
|
||||
**Windows:**
|
||||
```cmd
|
||||
ipconfig
|
||||
```
|
||||
|
||||
**Linux/macOS:**
|
||||
```bash
|
||||
ip addr show # Linux
|
||||
ifconfig # macOS/Linux
|
||||
```
|
||||
|
||||
Look for the `inet` (IPv4) or `inet6` (IPv6) addresses associated with your network interfaces.
|
||||
|
||||
## Related Discussion
|
||||
|
||||
This feature was implemented to address: https://github.com/rustdesk/rustdesk/discussions/2286
|
||||
@@ -11,7 +11,7 @@ use uuid::Uuid;
|
||||
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
anyhow::{self, bail},
|
||||
anyhow::{self, bail, Context},
|
||||
config::{
|
||||
self, keys::*, option2bool, use_ws, Config, CONNECT_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT,
|
||||
},
|
||||
@@ -749,6 +749,9 @@ impl RendezvousMediator {
|
||||
}
|
||||
}
|
||||
|
||||
// Socket backlog value used for TCP listeners, matching the value in hbb_common::tcp
|
||||
const SOCKET_BACKLOG: u32 = 128;
|
||||
|
||||
fn get_direct_port() -> i32 {
|
||||
let mut port = Config::get_option("direct-access-port")
|
||||
.parse::<i32>()
|
||||
@@ -759,6 +762,37 @@ fn get_direct_port() -> i32 {
|
||||
port
|
||||
}
|
||||
|
||||
async fn listen_with_bind_interface(port: u16) -> ResultType<hbb_common::tokio::net::TcpListener> {
|
||||
use hbb_common::tokio::net::{TcpListener, TcpSocket};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
let bind_interface = Config::get_option("bind-interface");
|
||||
|
||||
if bind_interface.is_empty() {
|
||||
// Use the default listen_any behavior
|
||||
hbb_common::tcp::listen_any(port).await
|
||||
} else {
|
||||
// Parse the bind interface address
|
||||
let addr: IpAddr = bind_interface.parse()
|
||||
.with_context(|| format!("Invalid bind interface address: {}", bind_interface))?;
|
||||
let socket_addr = SocketAddr::new(addr, port);
|
||||
|
||||
// Create and bind socket
|
||||
let socket = match socket_addr {
|
||||
SocketAddr::V4(..) => TcpSocket::new_v4()?,
|
||||
SocketAddr::V6(..) => TcpSocket::new_v6()?,
|
||||
};
|
||||
|
||||
// Set socket options
|
||||
#[cfg(all(unix, not(target_os = "illumos")))]
|
||||
allow_err!(socket.set_reuseport(true));
|
||||
allow_err!(socket.set_reuseaddr(true));
|
||||
|
||||
socket.bind(socket_addr)?;
|
||||
Ok(socket.listen(SOCKET_BACKLOG)?)
|
||||
}
|
||||
}
|
||||
|
||||
async fn direct_server(server: ServerPtr) {
|
||||
let mut listener = None;
|
||||
let mut port = 0;
|
||||
@@ -769,7 +803,7 @@ async fn direct_server(server: ServerPtr) {
|
||||
) || option2bool("stop-service", &Config::get_option("stop-service"));
|
||||
if !disabled && listener.is_none() {
|
||||
port = get_direct_port();
|
||||
match hbb_common::tcp::listen_any(port as _).await {
|
||||
match listen_with_bind_interface(port as _).await {
|
||||
Ok(l) => {
|
||||
listener = Some(l);
|
||||
log::info!(
|
||||
@@ -904,3 +938,27 @@ async fn udp_nat_listen(
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::IpAddr;
|
||||
|
||||
#[test]
|
||||
fn test_bind_interface_parsing() {
|
||||
// Test valid IPv4 addresses
|
||||
assert!("192.168.1.100".parse::<IpAddr>().is_ok());
|
||||
assert!("10.0.0.1".parse::<IpAddr>().is_ok());
|
||||
assert!("127.0.0.1".parse::<IpAddr>().is_ok());
|
||||
|
||||
// Test valid IPv6 addresses
|
||||
assert!("::1".parse::<IpAddr>().is_ok());
|
||||
assert!("fe80::1".parse::<IpAddr>().is_ok());
|
||||
assert!("2001:db8::1".parse::<IpAddr>().is_ok());
|
||||
|
||||
// Test invalid addresses
|
||||
assert!("invalid".parse::<IpAddr>().is_err());
|
||||
assert!("999.999.999.999".parse::<IpAddr>().is_err());
|
||||
assert!("".parse::<IpAddr>().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user