Compare commits

..

1 commit

Author SHA1 Message Date
c6cd325748 Package lanshare
Signed-off-by: Marcel Müller <neikos@neikos.email>
2026-02-09 14:09:57 +01:00

View file

@ -70,15 +70,6 @@ struct Args {
/// connect via ws:// instead. /// connect via ws:// instead.
#[arg(long, default_value_t = 8081, value_name = "PORT")] #[arg(long, default_value_t = 8081, value_name = "PORT")]
http_port: u16, http_port: u16,
/// Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IPs
///
/// Enable this when running behind a reverse proxy (e.g. nginx, caddy,
/// traefik). The server will use the IP from proxy headers instead of
/// the direct connection address. Do NOT enable this without a trusted
/// proxy, as clients can spoof these headers.
#[arg(long)]
trust_proxy: bool,
} }
/// Messages sent between peers via the signaling server /// Messages sent between peers via the signaling server
@ -164,14 +155,12 @@ struct PeerState {
/// Application state shared across all connections /// Application state shared across all connections
struct AppState { struct AppState {
peers: DashMap<String, PeerState>, peers: DashMap<String, PeerState>,
trust_proxy: bool,
} }
impl AppState { impl AppState {
fn new(trust_proxy: bool) -> Self { fn new() -> Self {
Self { Self {
peers: DashMap::new(), peers: DashMap::new(),
trust_proxy,
} }
} }
@ -213,7 +202,7 @@ async fn main() {
) )
.init(); .init();
let state = Arc::new(AppState::new(args.trust_proxy)); let state = Arc::new(AppState::new());
let app = Router::new() let app = Router::new()
.route("/", get(serve_index)) .route("/", get(serve_index))
@ -255,9 +244,6 @@ async fn main() {
} }
} }
} }
if args.trust_proxy {
println!(" \x1b[1;36mProxy mode:\x1b[0m trusting X-Forwarded-For / X-Real-IP headers");
}
println!(); println!();
println!(" \x1b[90mOpen this URL in browsers on your local network.\x1b[0m"); println!(" \x1b[90mOpen this URL in browsers on your local network.\x1b[0m");
println!(" \x1b[90mPress Ctrl+C to stop the server.\x1b[0m"); println!(" \x1b[90mPress Ctrl+C to stop the server.\x1b[0m");
@ -304,47 +290,14 @@ async fn serve_index() -> impl IntoResponse {
async fn ws_handler( async fn ws_handler(
ws: WebSocketUpgrade, ws: WebSocketUpgrade,
ConnectInfo(addr): ConnectInfo<SocketAddr>, ConnectInfo(addr): ConnectInfo<SocketAddr>,
headers: axum::http::HeaderMap,
State(state): State<Arc<AppState>>, State(state): State<Arc<AppState>>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let peer_ip = if state.trust_proxy { ws.on_upgrade(move |socket| handle_socket(socket, state, addr))
extract_proxy_ip(&headers).unwrap_or_else(|| addr.ip().to_string())
} else {
addr.ip().to_string()
};
ws.on_upgrade(move |socket| handle_socket(socket, state, peer_ip))
} }
/// Extract the client IP from proxy headers. async fn handle_socket(socket: WebSocket, state: Arc<AppState>, addr: SocketAddr) {
/// Checks X-Forwarded-For first (uses the leftmost/first IP), then X-Real-IP.
fn extract_proxy_ip(headers: &axum::http::HeaderMap) -> Option<String> {
// X-Forwarded-For: client, proxy1, proxy2
if let Some(forwarded_for) = headers.get("x-forwarded-for") {
if let Ok(value) = forwarded_for.to_str() {
if let Some(first_ip) = value.split(',').next() {
let trimmed = first_ip.trim();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
}
}
// X-Real-IP: client
if let Some(real_ip) = headers.get("x-real-ip") {
if let Ok(value) = real_ip.to_str() {
let trimmed = value.trim();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
}
None
}
async fn handle_socket(socket: WebSocket, state: Arc<AppState>, peer_ip: String) {
let peer_id = Uuid::new_v4().to_string(); let peer_id = Uuid::new_v4().to_string();
let peer_ip = addr.ip().to_string();
let (tx, _) = broadcast::channel::<SignalMessage>(64); let (tx, _) = broadcast::channel::<SignalMessage>(64);
// Add peer to state with default name // Add peer to state with default name