mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-07-29 06:31:45 +08:00
feat: VNC、RTSP 服务支持监听 ipv6 地址
This commit is contained in:
@@ -8,6 +8,7 @@ use tokio::sync::{broadcast, Mutex, RwLock};
|
||||
|
||||
use crate::config::RtspConfig;
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::utils::{bind_socket_addr, bind_tcp_listener};
|
||||
use crate::video::VideoStreamManager;
|
||||
|
||||
use super::auth::{extract_basic_auth, rtsp_auth_credentials};
|
||||
@@ -73,13 +74,18 @@ impl RtspService {
|
||||
tracing::debug!("Failed to request keyframe on RTSP start: {}", err);
|
||||
}
|
||||
|
||||
let bind_addr: SocketAddr = format!("{}:{}", config.bind, config.port)
|
||||
.parse()
|
||||
let bind_addr = bind_socket_addr(&config.bind, config.port)
|
||||
.map_err(|e| AppError::BadRequest(format!("Invalid RTSP bind address: {}", e)))?;
|
||||
|
||||
let listener = TcpListener::bind(bind_addr).await.map_err(|e| {
|
||||
let listener = bind_tcp_listener(bind_addr).map_err(|e| {
|
||||
AppError::Io(io::Error::new(e.kind(), format!("RTSP bind failed: {}", e)))
|
||||
})?;
|
||||
let listener = TcpListener::from_std(listener).map_err(|e| {
|
||||
AppError::Io(io::Error::new(
|
||||
e.kind(),
|
||||
format!("RTSP listener setup failed: {}", e),
|
||||
))
|
||||
})?;
|
||||
|
||||
let service_config = self.config.clone();
|
||||
let video_manager = self.video_manager.clone();
|
||||
|
||||
@@ -15,3 +15,38 @@ pub use host::{hostname_from_etc, hostname_uname};
|
||||
pub use net::{bind_tcp_listener, bind_udp_socket};
|
||||
pub use serial::list_serial_ports;
|
||||
pub use throttle::LogThrottler;
|
||||
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
pub fn bind_socket_addr(bind: &str, port: u16) -> Result<SocketAddr, std::net::AddrParseError> {
|
||||
bind.parse::<IpAddr>().map(|ip| SocketAddr::new(ip, port))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::bind_socket_addr;
|
||||
|
||||
#[test]
|
||||
fn ipv6_bind_socket_addr_formats_ipv4_and_ipv6() {
|
||||
assert_eq!(
|
||||
bind_socket_addr("0.0.0.0", 5900).unwrap().to_string(),
|
||||
"0.0.0.0:5900"
|
||||
);
|
||||
assert_eq!(
|
||||
bind_socket_addr("127.0.0.1", 5900).unwrap().to_string(),
|
||||
"127.0.0.1:5900"
|
||||
);
|
||||
assert_eq!(
|
||||
bind_socket_addr("::", 8554).unwrap().to_string(),
|
||||
"[::]:8554"
|
||||
);
|
||||
assert_eq!(
|
||||
bind_socket_addr("::1", 8554).unwrap().to_string(),
|
||||
"[::1]:8554"
|
||||
);
|
||||
assert_eq!(
|
||||
bind_socket_addr("2001:db8::1", 8554).unwrap().to_string(),
|
||||
"[2001:db8::1]:8554"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::config::{VncConfig, VncEncoding};
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::hid::HidController;
|
||||
use crate::stream::mjpeg::ClientGuard;
|
||||
use crate::utils::{bind_socket_addr, bind_tcp_listener};
|
||||
use crate::video::codec::{BitratePreset, VideoCodecType};
|
||||
use crate::video::stream_manager::VideoStreamManager;
|
||||
|
||||
@@ -108,15 +109,20 @@ impl VncService {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let bind_addr: SocketAddr = format!("{}:{}", config.bind, config.port)
|
||||
.parse()
|
||||
let bind_addr = bind_socket_addr(&config.bind, config.port)
|
||||
.map_err(|e| AppError::BadRequest(format!("Invalid VNC bind address: {}", e)))?;
|
||||
let listener = TcpListener::bind(bind_addr).await.map_err(|e| {
|
||||
let listener = bind_tcp_listener(bind_addr).map_err(|e| {
|
||||
AppError::Io(std::io::Error::new(
|
||||
e.kind(),
|
||||
format!("VNC bind failed: {}", e),
|
||||
))
|
||||
})?;
|
||||
let listener = TcpListener::from_std(listener).map_err(|e| {
|
||||
AppError::Io(std::io::Error::new(
|
||||
e.kind(),
|
||||
format!("VNC listener setup failed: {}", e),
|
||||
))
|
||||
})?;
|
||||
|
||||
let config_ref = self.config.clone();
|
||||
let video_manager = self.video_manager.clone();
|
||||
|
||||
@@ -1335,4 +1335,82 @@ mod tests {
|
||||
};
|
||||
assert!(update.validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_bind_vnc_config_accepts_ipv6_literals() {
|
||||
for bind in ["::", "::1", "2001:db8::1"] {
|
||||
let update = VncConfigUpdate {
|
||||
enabled: None,
|
||||
bind: Some(bind.to_string()),
|
||||
port: Some(5900),
|
||||
encoding: None,
|
||||
jpeg_quality: None,
|
||||
allow_one_client: None,
|
||||
password: None,
|
||||
};
|
||||
assert!(
|
||||
update.validate().is_ok(),
|
||||
"bind address should pass: {bind}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_bind_vnc_config_rejects_non_ip_or_socket_syntax() {
|
||||
for bind in ["localhost", "127.0.0.1:5900", "[::1]:5900"] {
|
||||
let update = VncConfigUpdate {
|
||||
enabled: None,
|
||||
bind: Some(bind.to_string()),
|
||||
port: Some(5900),
|
||||
encoding: None,
|
||||
jpeg_quality: None,
|
||||
allow_one_client: None,
|
||||
password: None,
|
||||
};
|
||||
assert!(
|
||||
update.validate().is_err(),
|
||||
"bind address should fail: {bind}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_bind_rtsp_config_accepts_ipv6_literals() {
|
||||
for bind in ["::", "::1", "2001:db8::1"] {
|
||||
let update = RtspConfigUpdate {
|
||||
enabled: None,
|
||||
bind: Some(bind.to_string()),
|
||||
port: Some(8554),
|
||||
path: None,
|
||||
allow_one_client: None,
|
||||
codec: None,
|
||||
username: None,
|
||||
password: None,
|
||||
};
|
||||
assert!(
|
||||
update.validate().is_ok(),
|
||||
"bind address should pass: {bind}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_bind_rtsp_config_rejects_non_ip_or_socket_syntax() {
|
||||
for bind in ["localhost", "127.0.0.1:8554", "[::1]:8554"] {
|
||||
let update = RtspConfigUpdate {
|
||||
enabled: None,
|
||||
bind: Some(bind.to_string()),
|
||||
port: Some(8554),
|
||||
path: None,
|
||||
allow_one_client: None,
|
||||
codec: None,
|
||||
username: None,
|
||||
password: None,
|
||||
};
|
||||
assert!(
|
||||
update.validate().is_err(),
|
||||
"bind address should fail: {bind}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user