use std::{
io::{self, Read, Write},
net::{TcpStream, ToSocketAddrs, UdpSocket},
os::unix::io::AsRawFd,
thread::sleep,
time::Duration,
};
use libafl::{HasMetadata, executors::ExitKind, inputs::HasTargetBytes};
use libafl_bolts::AsSlice;
use libc::{self, socklen_t};
use log::error;
use crate::tavern::option::{NetworkClientOptions, Protocol};
#[derive(Debug)]
enum Connection {
Tcp(TcpStream),
Udp(UdpSocket),
}
#[derive(Debug)]
pub struct NetworkClient {
protocol: Protocol,
timeout: Duration,
connection: Option<Connection>,
server_addr: Option<String>,
}
impl NetworkClient {
pub fn new(protocol: Protocol, timeout_usecs: u64) -> Self {
Self {
protocol,
timeout: Duration::from_micros(timeout_usecs),
connection: None,
server_addr: None,
}
}
fn set_linger(&self, fd: i32) -> io::Result<()> {
let linger = libc::linger {
l_onoff: 1, l_linger: 0, };
let result = unsafe {
libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_LINGER,
&linger as *const _ as *const libc::c_void,
size_of::<libc::linger>() as socklen_t,
)
};
if result < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn connect_to_server(&mut self, host: &str, port: u16) -> io::Result<()> {
let addr = format!("{}:{}", host, port);
self.server_addr = Some(addr.clone());
match self.protocol {
Protocol::Tcp => {
let addr = addr.to_socket_addrs()?.next().ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to resolve address")
})?;
let mut last_error = None;
for _ in 0..1000 {
match TcpStream::connect_timeout(&addr, self.timeout) {
Ok(stream) => {
self.set_linger(stream.as_raw_fd())?;
stream.set_read_timeout(Some(self.timeout))?;
stream.set_write_timeout(Some(self.timeout))?;
self.connection = Some(Connection::Tcp(stream));
return Ok(());
}
Err(e) => {
last_error = Some(e);
println!(
"Failed to connect to {}: {}",
addr,
last_error.as_ref().unwrap()
);
sleep(Duration::from_micros(1000));
}
}
}
Err(last_error.unwrap_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to connect after retries")
}))
}
Protocol::Udp => {
let socket = UdpSocket::bind("0.0.0.0:0")?;
self.set_linger(socket.as_raw_fd())?;
socket.set_read_timeout(Some(self.timeout))?;
socket.set_write_timeout(Some(self.timeout))?;
self.connection = Some(Connection::Udp(socket));
Ok(())
}
}
}
pub fn send_message(&mut self, message: &[u8]) -> io::Result<()> {
match (&mut self.connection, self.server_addr.as_ref()) {
(Some(Connection::Tcp(stream)), _) => {
stream.write_all(message)?;
}
(Some(Connection::Udp(socket)), Some(addr)) => {
socket.send_to(message, addr)?;
}
_ => {
return Err(io::Error::new(
io::ErrorKind::NotConnected,
"No active connection",
));
}
}
Ok(())
}
#[allow(unused)]
fn receive_response(&mut self) -> io::Result<Vec<u8>> {
let mut buffer = vec![0; 2048];
let n = match &mut self.connection {
Some(Connection::Tcp(stream)) => stream.read(&mut buffer)?,
Some(Connection::Udp(socket)) => {
let (n, _) = socket.recv_from(&mut buffer)?;
n
}
None => {
return Err(io::Error::new(
io::ErrorKind::NotConnected,
"No active connection",
));
}
};
buffer.truncate(n);
Ok(buffer)
}
}
pub fn create_default_harness<S, I>(
network: &NetworkClientOptions,
) -> impl FnMut(&mut S, &I) -> ExitKind
where
S: HasMetadata,
I: HasTargetBytes,
{
let mut client = NetworkClient::new(network.protocol(), network.sleep);
move |_state: &mut S, input: &I| {
let mut do_send = false;
for _ in 0..network.try_time {
if let Err(_) = client.connect_to_server(&network.host, network.port) {
error!("Failed to connect to server");
sleep(Duration::from_secs(1));
continue;
}
if let Err(_) = client.send_message(input.target_bytes().as_slice()) {
error!("Failed to send message to server");
sleep(Duration::from_secs(1));
continue;
}
do_send = true;
break;
}
if !do_send {
panic!(
"Failed to send message to server in {} tries",
network.try_time
)
}
sleep(Duration::from_nanos(network.sleep));
ExitKind::Ok
}
}