mirror of
https://github.com/PatchRequest/BusyWork
synced 2026-06-09 16:04:04 +00:00
4b72c45c57
Inter-task chaining: a 256-byte ScratchBuffer flows through all tasks in a single run() call. Each task reads from it (blend_into) and writes back results (absorb), creating a data-dependency chain where task N's output influences task N+1's computation. An analyzer can no longer identify individual tasks as isolated blocks. Work data in all tasks: all 84 tasks now actively consume fed work data. Filesystem tasks derive skip offsets and iteration biases from work data. Registry tasks bias subkey/value enumeration counts. WinAPI tasks offset starting indices for window/process/metric enumeration. Network tasks select starting host indices and blend work data into response buffers. New COM/WMI category (8 tasks): WQL queries against Win32_Process, Win32_OperatingSystem, Win32_ComputerSystem, Win32_NetworkAdapterConfiguration, Win32_LogicalDisk, Win32_Service, Win32_BIOS, and Win32_Processor. All read-only. Uses COM automation with CoInitializeSecurity for reliable initialization across apartment models. 84 tasks across 8 categories. All 182 tests pass.
318 lines
11 KiB
Rust
318 lines
11 KiB
Rust
use crate::categories::Categories;
|
|
use crate::tasks::{ScratchBuffer, TaskDescriptor, TaskParams};
|
|
use crate::workdata::WorkData;
|
|
use rand::rngs::ThreadRng;
|
|
use rand::RngCore;
|
|
use std::hint::black_box;
|
|
use std::io::{Read, Write};
|
|
use std::net::{TcpStream, ToSocketAddrs, UdpSocket};
|
|
use std::os::windows::io::AsRawSocket;
|
|
|
|
#[link(name = "ws2_32")]
|
|
extern "system" {
|
|
fn setsockopt(s: usize, level: i32, optname: i32, optval: *const u8, optlen: i32) -> i32;
|
|
}
|
|
|
|
const SOL_SOCKET: i32 = 0xFFFF;
|
|
const SO_RCVTIMEO: i32 = 0x1006;
|
|
const SO_SNDTIMEO: i32 = 0x1005;
|
|
|
|
fn set_socket_timeouts(socket: &impl AsRawSocket, ms: u32) {
|
|
let raw = socket.as_raw_socket() as usize;
|
|
let val = ms.to_ne_bytes();
|
|
unsafe {
|
|
setsockopt(raw, SOL_SOCKET, SO_RCVTIMEO, val.as_ptr(), 4);
|
|
setsockopt(raw, SOL_SOCKET, SO_SNDTIMEO, val.as_ptr(), 4);
|
|
}
|
|
}
|
|
|
|
pub fn register() -> Vec<TaskDescriptor> {
|
|
vec![
|
|
TaskDescriptor {
|
|
name: "dns_lookups",
|
|
category: Categories::NETWORK,
|
|
func: dns_lookups,
|
|
},
|
|
TaskDescriptor {
|
|
name: "http_get",
|
|
category: Categories::NETWORK,
|
|
func: http_get,
|
|
},
|
|
TaskDescriptor {
|
|
name: "ntp_query",
|
|
category: Categories::NETWORK,
|
|
func: ntp_query,
|
|
},
|
|
TaskDescriptor {
|
|
name: "http_head_request",
|
|
category: Categories::NETWORK,
|
|
func: http_head_request,
|
|
},
|
|
TaskDescriptor {
|
|
name: "tcp_connect_probe",
|
|
category: Categories::NETWORK,
|
|
func: tcp_connect_probe,
|
|
},
|
|
TaskDescriptor {
|
|
name: "dns_varied_ports",
|
|
category: Categories::NETWORK,
|
|
func: dns_varied_ports,
|
|
},
|
|
TaskDescriptor {
|
|
name: "http_post_discard",
|
|
category: Categories::NETWORK,
|
|
func: http_post_discard,
|
|
},
|
|
]
|
|
}
|
|
|
|
fn dns_lookups(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let hosts = [
|
|
"google.com:80",
|
|
"microsoft.com:80",
|
|
"cloudflare.com:80",
|
|
"github.com:80",
|
|
"amazon.com:80",
|
|
"apple.com:80",
|
|
"mozilla.org:80",
|
|
"wikipedia.org:80",
|
|
"reddit.com:80",
|
|
"stackoverflow.com:80",
|
|
"twitter.com:80",
|
|
"linkedin.com:80",
|
|
"facebook.com:80",
|
|
"youtube.com:80",
|
|
"netflix.com:80",
|
|
"twitch.tv:80",
|
|
"spotify.com:80",
|
|
"dropbox.com:80",
|
|
"zoom.us:80",
|
|
"slack.com:80",
|
|
"discord.com:80",
|
|
"akamai.com:80",
|
|
"fastly.com:80",
|
|
"aws.amazon.com:80",
|
|
];
|
|
let start_idx = work.derive_usize(0) % hosts.len();
|
|
for i in 0..params.iterations.min(50) {
|
|
let host = hosts[(start_idx + i) % hosts.len()];
|
|
let _ = host.to_socket_addrs().map(|addrs| {
|
|
for addr in addrs {
|
|
let bytes = match addr {
|
|
std::net::SocketAddr::V4(v4) => v4.ip().octets().to_vec(),
|
|
std::net::SocketAddr::V6(v6) => v6.ip().octets().to_vec(),
|
|
};
|
|
scratch.absorb(&bytes);
|
|
black_box(addr);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
fn http_get(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let targets: &[(&str, u16, &str)] = &[
|
|
("httpbin.org", 80, "/get"),
|
|
("ip-api.com", 80, "/json"),
|
|
("ifconfig.me", 80, "/ip"),
|
|
("api.ipify.org", 80, "/"),
|
|
("httpbin.org", 80, "/headers"),
|
|
("httpbin.org", 80, "/user-agent"),
|
|
("worldtimeapi.org", 80, "/api/ip"),
|
|
("httpbin.org", 80, "/ip"),
|
|
("icanhazip.com", 80, "/"),
|
|
("checkip.amazonaws.com", 80, "/"),
|
|
("api.myip.com", 80, "/"),
|
|
];
|
|
let start_idx = work.derive_usize(0) % targets.len();
|
|
for i in 0..params.call_depth.min(5) {
|
|
let &(host, port, path) = &targets[(start_idx + i) % targets.len()];
|
|
let addr = format!("{}:{}", host, port);
|
|
let stream = match TcpStream::connect(&*addr) {
|
|
Ok(s) => s,
|
|
Err(_) => continue,
|
|
};
|
|
set_socket_timeouts(&stream, 3000);
|
|
let request = format!(
|
|
"GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
|
|
path, host
|
|
);
|
|
let mut stream = stream;
|
|
if stream.write_all(request.as_bytes()).is_err() {
|
|
continue;
|
|
}
|
|
let mut response = vec![0u8; 4096];
|
|
let _ = stream.read(&mut response);
|
|
work.blend_into(&mut response);
|
|
scratch.blend_into(&mut response);
|
|
scratch.absorb(&response[..response.len().min(256)]);
|
|
black_box(&response);
|
|
}
|
|
}
|
|
|
|
fn ntp_query(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let servers = [
|
|
"pool.ntp.org:123",
|
|
"time.google.com:123",
|
|
"time.cloudflare.com:123",
|
|
"time.windows.com:123",
|
|
"time.apple.com:123",
|
|
"time.nist.gov:123",
|
|
"ntp.ubuntu.com:123",
|
|
];
|
|
let start_idx = work.derive_usize(0) % servers.len();
|
|
for i in 0..params.call_depth.min(5) {
|
|
let server = servers[(start_idx + i) % servers.len()];
|
|
let socket = match UdpSocket::bind("0.0.0.0:0") {
|
|
Ok(s) => s,
|
|
Err(_) => continue,
|
|
};
|
|
set_socket_timeouts(&socket, 3000);
|
|
let mut packet = [0u8; 48];
|
|
packet[0] = 0x1B;
|
|
if socket.send_to(&packet, server).is_err() {
|
|
continue;
|
|
}
|
|
let mut response = [0u8; 48];
|
|
let _ = socket.recv_from(&mut response);
|
|
scratch.absorb(&response);
|
|
black_box(&response);
|
|
}
|
|
}
|
|
|
|
fn http_head_request(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let targets: &[(&str, u16, &str)] = &[
|
|
("httpbin.org", 80, "/get"),
|
|
("httpbin.org", 80, "/headers"),
|
|
("httpbin.org", 80, "/ip"),
|
|
("httpbin.org", 80, "/user-agent"),
|
|
("ip-api.com", 80, "/json"),
|
|
("ifconfig.me", 80, "/ip"),
|
|
("worldtimeapi.org", 80, "/api/ip"),
|
|
("checkip.amazonaws.com", 80, "/"),
|
|
("icanhazip.com", 80, "/"),
|
|
("api.ipify.org", 80, "/"),
|
|
];
|
|
let start_idx = work.derive_usize(0) % targets.len();
|
|
for i in 0..params.call_depth.min(5) {
|
|
let &(host, port, path) = &targets[(start_idx + i) % targets.len()];
|
|
let addr = format!("{}:{}", host, port);
|
|
let stream = match TcpStream::connect(&*addr) {
|
|
Ok(s) => s,
|
|
Err(_) => continue,
|
|
};
|
|
set_socket_timeouts(&stream, 3000);
|
|
let request = format!(
|
|
"HEAD {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
|
|
path, host
|
|
);
|
|
let mut stream = stream;
|
|
if stream.write_all(request.as_bytes()).is_err() {
|
|
continue;
|
|
}
|
|
let mut response = vec![0u8; 2048];
|
|
let _ = stream.read(&mut response);
|
|
scratch.absorb(&response[..response.len().min(256)]);
|
|
black_box(&response);
|
|
}
|
|
}
|
|
|
|
fn tcp_connect_probe(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let targets: &[(&str, u16)] = &[
|
|
("google.com", 80),
|
|
("google.com", 443),
|
|
("cloudflare.com", 80),
|
|
("cloudflare.com", 443),
|
|
("github.com", 443),
|
|
("microsoft.com", 80),
|
|
("microsoft.com", 443),
|
|
("amazon.com", 443),
|
|
("1.1.1.1", 53),
|
|
("8.8.8.8", 53),
|
|
];
|
|
let start_idx = work.derive_usize(0) % targets.len();
|
|
for i in 0..params.iterations.min(10) {
|
|
let &(host, port) = &targets[(start_idx + i) % targets.len()];
|
|
let addr = format!("{}:{}", host, port);
|
|
if let Ok(stream) = TcpStream::connect(&*addr) {
|
|
set_socket_timeouts(&stream, 2000);
|
|
let marker = [host.as_bytes()[0], port as u8];
|
|
scratch.absorb(&marker);
|
|
black_box(&stream);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn dns_varied_ports(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let hosts = [
|
|
"example.com:21",
|
|
"example.com:22",
|
|
"example.com:25",
|
|
"example.com:53",
|
|
"example.com:110",
|
|
"example.com:143",
|
|
"example.com:443",
|
|
"example.com:993",
|
|
"example.com:8080",
|
|
"example.com:8443",
|
|
"example.org:80",
|
|
"example.org:443",
|
|
"example.net:80",
|
|
"example.net:25",
|
|
"example.net:8080",
|
|
];
|
|
let start_idx = work.derive_usize(0) % hosts.len();
|
|
for i in 0..params.iterations.min(40) {
|
|
let host = hosts[(start_idx + i) % hosts.len()];
|
|
let _ = host.to_socket_addrs().map(|addrs| {
|
|
for addr in addrs {
|
|
let bytes = match addr {
|
|
std::net::SocketAddr::V4(v4) => v4.ip().octets().to_vec(),
|
|
std::net::SocketAddr::V6(v6) => v6.ip().octets().to_vec(),
|
|
};
|
|
scratch.absorb(&bytes);
|
|
black_box(addr);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
fn http_post_discard(params: &TaskParams, rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
|
|
let targets: &[(&str, u16, &str, &str)] = &[
|
|
("httpbin.org", 80, "/post", "POST"),
|
|
("httpbin.org", 80, "/anything", "POST"),
|
|
("httpbin.org", 80, "/put", "PUT"),
|
|
("httpbin.org", 80, "/anything", "PUT"),
|
|
("httpbin.org", 80, "/patch", "PATCH"),
|
|
];
|
|
|
|
let body_size = params.buffer_size.min(1024);
|
|
let mut body = vec![0u8; body_size];
|
|
rng.fill_bytes(&mut body);
|
|
work.blend_into(&mut body);
|
|
scratch.blend_into(&mut body);
|
|
let body_hex: String = body.iter().map(|b| format!("{:02x}", b)).collect();
|
|
|
|
let start_idx = work.derive_usize(0) % targets.len();
|
|
for i in 0..params.call_depth.min(5) {
|
|
let &(host, port, path, method) = &targets[(start_idx + i) % targets.len()];
|
|
let addr = format!("{}:{}", host, port);
|
|
let stream = match TcpStream::connect(&*addr) {
|
|
Ok(s) => s,
|
|
Err(_) => continue,
|
|
};
|
|
set_socket_timeouts(&stream, 3000);
|
|
let request = format!(
|
|
"{} {} HTTP/1.1\r\nHost: {}\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
|
method, path, host, body_hex.len(), body_hex
|
|
);
|
|
let mut stream = stream;
|
|
if stream.write_all(request.as_bytes()).is_err() {
|
|
continue;
|
|
}
|
|
let mut response = vec![0u8; 4096];
|
|
let _ = stream.read(&mut response);
|
|
scratch.absorb(&response[..response.len().min(256)]);
|
|
black_box(&response);
|
|
}
|
|
}
|