Skip to content

Commit

Permalink
feat: Convert IPv4 embedded in IPv6 into IPv4 at the beginning
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernauer committed Apr 27, 2022
1 parent e58a397 commit 270acc9
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 19 deletions.
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ edition = "2018"

[dependencies]
clap = { version = "3.0.7", features = ["derive"] }
vncserver = "0.2.2"
rusttype = "0.9.2"
number_prefix = "0.4"
number_prefix = "0.4.0"
prometheus = "0.13.0"
prometheus_exporter = "0.8.3"
vncserver = "0.2.2"

[dev-dependencies]
rstest = "0.12.0"

[profile.dev]
opt-level = 3
Expand Down
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@ fn main() {
vnc_server.run();
});

network_thread.join().expect("Failed to join network thread");
network_thread
.join()
.expect("Failed to join network thread");
}
56 changes: 49 additions & 7 deletions src/network.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::{IpAddr, Ipv4Addr, TcpListener};
use std::net::TcpStream;
use std::str;
use std::sync::Arc;
Expand Down Expand Up @@ -45,21 +45,26 @@ impl<'a> Network<'a> {

for stream in listener.incoming() {
let stream = stream.expect("Failed to get tcp stream from listener");
let ip = stream
.peer_addr()
.expect("Failed to get peer address from tcp connection")
.ip();
// If you connect via IPv4 you often show up as embedded inside an IPv6 address
// Extracting the embedded information here, so we get the real (TM) address
let ip = ip_to_canonical(ip);

self.statistics
.inc_connections(stream.peer_addr().expect("Failed to get peer address from tcp connection").ip());
self.statistics.inc_connections(ip);

let fb = Arc::clone(&self.fb);
let statistics = Arc::clone(&self.statistics);
thread::spawn(move || {
handle_connection(stream, fb, statistics);
handle_connection(stream, ip, fb, statistics);
});
}
}
}

fn handle_connection(mut stream: TcpStream, fb: Arc<FrameBuffer>, statistics: Arc<Statistics>) {
let ip = stream.peer_addr().expect("Failed to get peer address from tcp connection").ip();
fn handle_connection(mut stream: TcpStream, ip: IpAddr, fb: Arc<FrameBuffer>, statistics: Arc<Statistics>) {
let mut buffer = [0u8; NETWORK_BUFFER_SIZE];

loop {
Expand Down Expand Up @@ -222,7 +227,9 @@ fn handle_connection(mut stream: TcpStream, fb: Arc<FrameBuffer>, statistics: Ar
i += 1;
if buffer[i] == b'P' {
i += 1;
stream.write_all(HELP_TEXT).expect("Failed to write bytes to tcp socket");
stream
.write_all(HELP_TEXT)
.expect("Failed to write bytes to tcp socket");
}
}
}
Expand All @@ -240,3 +247,38 @@ fn from_hex_char(char: u8) -> u8 {
_ => 0,
}
}

/// TODO: Switch to official ip.to_canonical() method when it is stable. **If** it gets stable sometime ;)
/// See https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.to_canonical
fn ip_to_canonical(ip: IpAddr) -> IpAddr {
match ip {
IpAddr::V4(_) => ip,
IpAddr::V6(v6) => match v6.octets() {
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => {
IpAddr::V4(Ipv4Addr::new(a, b, c, d))
}
_ => ip,
},
}
}

#[cfg(test)]
mod tests {
use crate::network::*;
use rstest::rstest;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

#[rstest]
#[case(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0")]
#[case(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1")]
#[case(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0)), "fe80::")]
#[case(
IpAddr::V6(Ipv6Addr::new(0x2001, 0x0db8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334)),
"2001:db8:85a3::8a2e:370:7334"
)]
#[case(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 0, 1)), "0.0.0.1")]
#[case(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 127 << 8, 1)), "127.0.0.1")]
fn test_ip_to_string_respect_mapped_v4(#[case] input: IpAddr, #[case] expected: String) {
assert_eq!(expected, ip_to_canonical(input).to_string());
}
}
10 changes: 1 addition & 9 deletions src/statistics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl Statistics {
.lock()
.unwrap()
.keys()
.filter(|ip| ip.is_ipv4() || (ip.is_ipv6() && is_mapped_to_ipv6(ip)))
.filter(|ip| ip.is_ipv4())
.count() as u32
}

Expand Down Expand Up @@ -271,11 +271,3 @@ pub fn start_prometheus_server(prometheus_listen_address: &str) {
.expect("Failed to start prometheus exporter");
println!("Started Prometheus Exporter on {prometheus_listen_address}");
}

fn is_mapped_to_ipv6(ip: &IpAddr) -> bool {
match ip {
// 5 * 16 `0` bits, 16 `1` bits, leftover is actual IPv4 addr
IpAddr::V6(ip) => matches!(ip.segments(), [0, 0, 0, 0, 0, 0xFFFF, ..]),
_ => false,
}
}

0 comments on commit 270acc9

Please sign in to comment.