Stash commit 📦

This commit is contained in:
Sivert V. Sæther 2023-01-13 17:55:57 +01:00
parent 23ce3ef304
commit b49f55796f
6 changed files with 149 additions and 81 deletions

View File

@ -1,3 +1,4 @@
#[allow(clippy::upper_case_acronyms)]
use std::{ use std::{
io, net::IpAddr, io, net::IpAddr,
time::Duration, time::Duration,

View File

@ -1,9 +1,6 @@
use std::{ use std::{
sync::{Mutex, Arc}, sync::{Mutex, Arc}, time::Duration, thread::sleep,
time::Duration, net::{Ipv4Addr, IpAddr}, vec::Vec, io};
thread::sleep,
net::IpAddr,
vec::Vec, io};
use log::{trace, warn, error}; use log::{trace, warn, error};
use tokio::{ use tokio::{
@ -12,13 +9,8 @@ use tokio::{
use nbd::server::Blocks; use nbd::server::Blocks;
use icmp::IcmpSocket; use icmp::IcmpSocket;
use crate::{ use crate::{
ICMP_PACKET, IPStore, ICMP_PACKET, BYTE_COUNT, PACKET_SIZE, SIZE,
pinger::connect, IPStore, checksum, connect, get_rt};
checksum,
get_rt};
const SIZE: usize = 64; // Bits of data in each ping
static WORD_COUNT: usize = SIZE / 8; // Number of 16-bit words in each ping
type Message = (usize, usize, Vec<u8>); // Message type for the write channel type Message = (usize, usize, Vec<u8>); // Message type for the write channel
@ -56,7 +48,7 @@ impl PingStore {
let offset = store.pings.lock().unwrap().len() * 7; let offset = store.pings.lock().unwrap().len() * 7;
let mut ping = Ping::new(); let mut ping = Ping::new();
for j in 0..7 { for j in 0..7 {
ping.add(ips.dsts[offset + j].ip); ping.add(IpAddr::V4(ips.dsts[offset + j].ip));
} }
store.pings.lock().unwrap().push(ping); store.pings.lock().unwrap().push(ping);
store.size += 1; store.size += 1;
@ -124,7 +116,7 @@ impl PingStore {
impl Blocks for PingStore { impl Blocks for PingStore {
fn read_at(&self, buf: &mut [u8], off: u64) -> io::Result<()> { fn read_at(&self, buf: &mut [u8], off: u64) -> io::Result<()> {
let addr = (off as usize).div_floor(WORD_COUNT); let addr = (off as usize).div_floor(BYTE_COUNT);
let buflen = buf.len(); let buflen = buf.len();
let mut res = vec![]; let mut res = vec![];
for pingspan in 0..buflen.div_ceil(SIZE) { for pingspan in 0..buflen.div_ceil(SIZE) {
@ -136,10 +128,10 @@ impl Blocks for PingStore {
} }
fn write_at(&self, buf: &[u8], off: u64) -> io::Result<()> { fn write_at(&self, buf: &[u8], off: u64) -> io::Result<()> {
let addr = (off as usize).div_ceil(WORD_COUNT); let addr = (off as usize).div_ceil(BYTE_COUNT);
let buflen = buf.len(); let buflen = buf.len();
get_rt("channel pusher", num_cpus::get().div_floor(3) + 1).block_on( get_rt("channel pusher", num_cpus::get().div_floor(3) + 1).block_on(
self.write(buf, addr, (addr * WORD_COUNT) - off as usize, buflen))?; self.write(buf, addr, (addr * BYTE_COUNT) - off as usize, buflen))?;
Ok(()) Ok(())
} }

View File

@ -1,20 +1,68 @@
#![feature(bigint_helper_methods)] #![feature(bigint_helper_methods)]
#![feature(async_closure)] //#![feature(async_closure)]
#![feature(int_roundings)] #![feature(int_roundings)]
#![feature(never_type)] #![feature(never_type)]
#![feature(ip)] #![feature(ip)]
pub mod scanner; pub mod scanner;
pub mod pinger; //pub mod pinger;
pub mod blocks; pub mod blocks;
pub mod store; mod store;
pub use blocks::PingStore; pub use blocks::PingStore;
pub use scanner::Scanner; pub use scanner::Scanner;
pub use pinger::{ //pub use pinger::Pinger;
ICMP_PACKET, TIMEOUT, Pinger};
pub use store::IPStore; pub use store::IPStore;
/// ICMP packet header template
pub const ICMP_PACKET: [u8; 8] = [
8, 0, // 8 Echo ping request, code 0
0, 0, // Index 2 and 3 are for ICMP checksum
0xde, 0xad, // Identifier
0, 1, // Sequence numbers
];
pub const SIZE: usize = 64; // Bytes of echo data in each ping
pub const PACKET_SIZE: usize = SIZE + 16; // Echo data size plus rest of ICMP packet
pub const BYTE_COUNT: usize = SIZE / 8;
pub static mut TIMEOUT: Option<std::time::Duration> = None;
/// Get an IcmpSocket from IpAddr and set the global timeout on the socket
pub fn connect(ip: std::net::IpAddr) -> icmp::IcmpSocket {
let sock = match icmp::IcmpSocket::connect(ip) {
Err(err) => panic!("Unable to open ICMP socket: {err:?}"),
Ok(sock) => sock,
};
let timeout = unsafe { TIMEOUT };
if timeout.is_none() {
return sock;
}
if let Err(err) = sock.set_write_timeout(timeout) {
log::debug!("unable to set write timeout on socket: {}", err);
};
if let Err(err) = sock.set_read_timeout(timeout) {
log::debug!("unable to set read timeout on socket: {}", err);
};
sock
}
#[inline] #[inline]
/// Get a random IPv4 address
pub fn rand_ip4() -> std::net::Ipv4Addr {
std::net::Ipv4Addr::new(rand::random(), rand::random(), rand::random(), rand::random())
}
#[inline]
/// Get a random globaly accessible IP address
pub fn rand_ip() -> std::net::Ipv4Addr {
let mut ip = rand_ip4();
while !ip.is_global() { ip = rand_ip4() };
//std::net::IpAddr::V4(ip)
ip
}
#[inline]
/// Calculate the "internet checksum"
pub fn checksum(bytes: &[u8]) -> [u8; 2] { pub fn checksum(bytes: &[u8]) -> [u8; 2] {
let mut calc = internet_checksum::Checksum::new(); let mut calc = internet_checksum::Checksum::new();
calc.add_bytes(bytes); calc.add_bytes(bytes);
@ -22,12 +70,13 @@ pub fn checksum(bytes: &[u8]) -> [u8; 2] {
} }
#[inline] #[inline]
/// Get a new tokio multi thread runtime
pub fn get_rt(name: &str, threads: usize) -> tokio::runtime::Runtime { pub fn get_rt(name: &str, threads: usize) -> tokio::runtime::Runtime {
match tokio::runtime::Builder::new_multi_thread() match tokio::runtime::Builder::new_multi_thread()
.worker_threads(threads) .worker_threads(threads)
.thread_name(name) .thread_name(name)
.build() { .build() {
Err(err) => panic!("Unable to build tokio runtime! {:?}", err), Err(err) => panic!("Unable to build tokio runtime! {err:?}"),
Ok(rt) => rt, Ok(rt) => rt,
} }
} }

View File

@ -6,40 +6,33 @@ use std::{
use log::{trace, debug, info}; use log::{trace, debug, info};
use icmp::IcmpSocket; use icmp::IcmpSocket;
use rand::random; use rand::random;
use crate::checksum; use crate::{
ICMP_PACKET, PACKET_SIZE,
pub static mut TIMEOUT: Option<Duration> = None; TIMEOUT, SIZE, checksum};
pub static ICMP_PACKET: [u8; 8] = [
8, 0, // 8 Echo ping request, code 0
0, 0, // Index 2 and 3 are for ICMP checksum
0xde, 0xad, // Identifier
0, 1, // Sequence numbers
];
pub struct PingResponse { pub struct PingResponse {
pub round_trip: usize, pub round_trip: usize,
pub corrupt: bool, pub corrupt: bool,
pub small: bool, pub small: bool,
pub data: [u8; 84], pub data: [u8; PACKET_SIZE],
} }
pub type PingResult = Result<PingResponse, io::Error>; pub type PingResult = Result<PingResponse, io::Error>;
pub struct Pinger { pub struct Pinger {
pub data: [u8; 64], pub data: [u8; SIZE],
pub seq: [u8; 2], pub seq: [u8; 2],
} }
impl Pinger { impl Pinger {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
data: [0; 64], data: [0; SIZE],
seq: [1, 0], seq: [1, 0],
} }
} }
pub fn data(mut self, data: [u8; 64]) -> Self { pub fn data(mut self, data: [u8; SIZE]) -> Self {
self.data = data; self.data = data;
self self
} }
@ -68,7 +61,7 @@ impl Pinger {
}; };
self.inc_seq(); self.inc_seq();
let mut response: [u8; 64 + 20] = [0; 64 + 20]; let mut response: [u8; PACKET_SIZE] = [0; PACKET_SIZE];
match socket.recv(&mut response) { match socket.recv(&mut response) {
Err(err) => { Err(err) => {
@ -78,7 +71,7 @@ impl Pinger {
Ok(size) => { Ok(size) => {
let mut result = PingResponse::new(response, start.elapsed().as_millis() as usize); let mut result = PingResponse::new(response, start.elapsed().as_millis() as usize);
if size != 84 { if size != PACKET_SIZE {
debug!("Didn't revice full response from \"{}\"", ip); debug!("Didn't revice full response from \"{}\"", ip);
trace!("Response in question: {:?}", response); trace!("Response in question: {:?}", response);
result.corrupt = true; result.corrupt = true;
@ -141,7 +134,7 @@ pub fn rand_ip() -> IpAddr {
} }
impl PingResponse { impl PingResponse {
fn new(data: [u8; 84], round_trip: usize) -> Self { fn new(data: [u8; PACKET_SIZE], round_trip: usize) -> Self {
Self { Self {
round_trip, round_trip,
corrupt: false, corrupt: false,
@ -153,7 +146,7 @@ impl PingResponse {
impl Default for PingResponse { impl Default for PingResponse {
fn default() -> Self { fn default() -> Self {
Self::new([0; 84], 0) Self::new([0; PACKET_SIZE], 0)
} }
} }

View File

@ -1,32 +1,43 @@
use std::{ use std::{
vec::Vec, net::{Ipv4Addr, IpAddr}, time::{Duration, Instant},
time::Duration, thread::sleep}; net::{Ipv4Addr, IpAddr},
thread::sleep, io,
vec::Vec};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use log::{trace, debug, info, error}; use log::{trace, debug, info, error};
use tokio::task; use icmp::IcmpSocket;
use tokio::{task, };
use crate::{ use crate::{
pinger::rand_ip, ICMP_PACKET, PACKET_SIZE, SIZE,
IPStore, Pinger}; IPStore, checksum, connect,
rand_ip};
//static PROBE_CHK: [u8; 2] = [0x51, 0x59]; static PROBE: [u8; SIZE] = [0x66; SIZE];
static PROBE: [u8; 64] = [0x66; 64];
#[derive(Clone)] #[derive(Clone)]
pub struct Scanner { pub struct Scanner {
pub dsts: Vec<Destination>, pub dsts: Vec<Destination>,
pub dead: Vec<IpAddr>, pub dead: Vec<Ipv4Addr>,
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]
pub struct Destination { pub struct Destination {
pub round_trip: usize, // Round trip in ms pub round_trip: usize, // Round trip in ms
pub small: bool, pub small: bool,
pub ip: IpAddr, pub ip: Ipv4Addr,
} }
pub type ScanResult = Result<Destination, IpAddr>; struct PingResponse {
pub round_trip: usize,
pub corrupt: bool,
pub small: bool,
pub data: [u8; PACKET_SIZE],
}
pub type PingResult = Result<PingResponse, io::Error>;
pub type ScanResult = Result<Destination, Ipv4Addr>;
impl Scanner { impl Scanner {
pub fn new() -> Self { pub fn new() -> Self {
@ -43,16 +54,16 @@ impl Scanner {
} }
#[inline] #[inline]
fn next_ip(&self) -> IpAddr { fn next_ip(&self) -> Ipv4Addr {
// let mut ip = rand_ip(); let mut ip = rand_ip();
let mut ip = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 138)); // let mut ip = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 138));
while self.scanned(&ip) { while self.scanned(&ip) {
ip = rand_ip(); } ip = rand_ip(); }
ip ip
} }
#[inline] #[inline]
fn scanned(&self, ip: &IpAddr) -> bool { fn scanned(&self, ip: &Ipv4Addr) -> bool {
for dead in &self.dead { for dead in &self.dead {
if ip == dead { return true; } if ip == dead { return true; }
} }
@ -64,43 +75,65 @@ impl Scanner {
false false
} }
async fn pop_futs(&mut self, futs: &mut Vec<task::JoinHandle<ScanResult>>) {
let fut = futs.pop().expect("Unable to pop of futures vec during mass_scan()!");
match fut.await {
Err(err) => {
error!("Joing future: {:?}", err);
},
Ok(res) => match res {
Ok(dst) => self.dsts.push(dst),
Err(ip) => self.dead.push(ip),
}
}
}
pub async fn mass_scan(&mut self, throttle: usize, parallel: usize, limit: usize, rand: bool) { pub async fn mass_scan(&mut self, throttle: usize, parallel: usize, limit: usize, rand: bool) {
info!("Starting mass scan with a limit of {} and {} to randomized IP order!", limit, rand); info!("Starting mass scan with a limit of {} and {} to randomized IP order!", limit, rand);
let duration = Duration::from_millis(throttle as u64); let duration = Duration::from_millis(throttle as u64);
let mut futs: Vec<task::JoinHandle<ScanResult>> = vec![]; let mut futs: Vec<task::JoinHandle<()>> = vec![];
let listner = task::spawn(listner());
for _i in 0..limit { for _i in 0..limit {
if futs.len() >= parallel { if futs.len() >= parallel {
self.pop_futs(&mut futs).await; pop_futs(&mut futs).await;
} }
futs.push(task::spawn(scan(self.next_ip()))); futs.push(task::spawn(ping(self.next_ip(), &PROBE)));
sleep(duration); sleep(duration);
} }
while !futs.is_empty() { while !futs.is_empty() { pop_futs(&mut futs).await; }
self.pop_futs(&mut futs).await } listner.abort();
} }
} }
pub async fn scan(ip: IpAddr) -> Result<Destination, IpAddr> { async fn pop_futs(futs: &mut Vec<task::JoinHandle<()>>) {
trace!("Scanning IP \"{}\"", ip); futs.pop().expect("Unable to pop of futures vec during mass_scan()!").await;
if let Ok(res) = Pinger::new().data(PROBE).ping(ip) { }
info!("Good result scanning IP \"{}\": {}ms", ip, res.round_trip);
if res.corrupt { return Err(ip) } async fn ping(ip: Ipv4Addr, data: &[u8]) {
Ok(Destination { ip, small: res.small, round_trip: res.round_trip }) trace!("Scanning IP \"{ip}\"");
} else { Err(ip) } let mut pack = ICMP_PACKET.to_vec();
pack[4..7].copy_from_slice(&ip.octets());
pack.append(&mut data.to_vec());
let checksum = checksum(&pack);
pack[2] = checksum[0];
pack[3] = checksum[1];
let mut socket = connect(IpAddr::V4(ip));
let start = Instant::now();
match socket.send(&pack) {
Ok(size) => if size != pack.len() { debug!("Sent {size} bytes of {} bytes to {ip}", pack.len()) },
Err(err) => debug!("Unable to ping IP {ip}: {err:?}"),
}
}
async fn listner() -> ! {
let socket = match IcmpSocket::connect(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))) {
Err(err) => panic!("Unable to open listening socket: {err:?}"),
Ok(sock) => sock,
};
let mut handles = vec![];
loop {
let mut response: [u8; PACKET_SIZE] = [0; PACKET_SIZE];
match socket.recv(&mut response) {
Ok(size) => handles.push(task::spawn(handler(response, size))),
Err(err) => info!("Error reading a ping reply: {err:?}"),
}
}
}
async fn handler(response: [u8; PACKET_SIZE], size: usize) {
todo!();
} }
impl Default for Scanner { impl Default for Scanner {

View File

@ -2,7 +2,7 @@ use std::{
fs::OpenOptions, fs::OpenOptions,
io::{Write, Read}, io::{Write, Read},
str::from_utf8, str::from_utf8,
net::IpAddr, net::Ipv4Addr,
vec::Vec}; vec::Vec};
use log::{trace, debug, error}; use log::{trace, debug, error};
@ -14,7 +14,7 @@ use crate::{
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct IPStore { pub struct IPStore {
pub dsts: Vec<Destination>, pub dsts: Vec<Destination>,
pub dead: Vec<IpAddr>, pub dead: Vec<Ipv4Addr>,
} }
impl IPStore { impl IPStore {