aha_misc/tavern/
tcp_udp.rs

1//! TCP && UDP harness
2
3use std::{
4    io::{self, Read, Write},
5    net::{TcpStream, ToSocketAddrs, UdpSocket},
6    os::unix::io::AsRawFd,
7    thread::sleep,
8    time::Duration,
9};
10
11use libafl::{HasMetadata, executors::ExitKind, inputs::HasTargetBytes};
12use libafl_bolts::AsSlice;
13use libc::{self, socklen_t};
14use log::error;
15
16use crate::tavern::option::{NetworkClientOptions, Protocol};
17
18/// Internal connection type representation.
19#[derive(Debug)]
20enum Connection {
21    /// TCP stream connection
22    Tcp(TcpStream),
23    /// UDP socket connection
24    Udp(UdpSocket),
25}
26
27/// A client for network-based fuzzing that supports both TCP and UDP protocols.
28#[derive(Debug)]
29pub struct NetworkClient {
30    /// The protocol to use (TCP or UDP)
31    protocol: Protocol,
32    /// Timeout for connection and I/O operations
33    timeout: Duration,
34    /// Current active connection
35    connection: Option<Connection>,
36    /// Target server address
37    server_addr: Option<String>,
38}
39
40impl NetworkClient {
41    /// Create a new NetworkClient with the specified protocol and timeout.
42    ///
43    /// # Arguments
44    /// * `protocol` - The network protocol to use (TCP or UDP)
45    /// * `timeout_usecs` - Connection and I/O timeout in microseconds
46    ///
47    /// # Returns
48    /// A new NetworkClient instance
49    pub fn new(protocol: Protocol, timeout_usecs: u64) -> Self {
50        Self {
51            protocol,
52            timeout: Duration::from_micros(timeout_usecs),
53            connection: None,
54            server_addr: None,
55        }
56    }
57
58    /// Configure the socket's SO_LINGER option.
59    ///
60    /// # Arguments
61    /// * `fd` - File descriptor of the socket to configure
62    ///
63    /// # Returns
64    /// `Ok(())` on success, or an error if the operation fails
65    ///
66    /// # Description
67    /// Sets the SO_LINGER option with a timeout of 0 seconds,
68    /// which causes the socket to close immediately on shutdown.
69    fn set_linger(&self, fd: i32) -> io::Result<()> {
70        let linger = libc::linger {
71            l_onoff: 1,  // 启用 SO_LINGER
72            l_linger: 0, // 设置超时时间为 0 秒
73        };
74
75        let result = unsafe {
76            libc::setsockopt(
77                fd,
78                libc::SOL_SOCKET,
79                libc::SO_LINGER,
80                &linger as *const _ as *const libc::c_void,
81                size_of::<libc::linger>() as socklen_t,
82            )
83        };
84
85        if result < 0 {
86            return Err(io::Error::last_os_error());
87        }
88        Ok(())
89    }
90
91    /// Connect to a target server.
92    ///
93    /// # Arguments
94    /// * `host` - Hostname or IP address of the target server
95    /// * `port` - Port number of the target server
96    ///
97    /// # Returns
98    /// `Ok(())` on successful connection, or an error if connection fails
99    ///
100    /// # Description
101    /// For TCP, this performs the actual connection and will retry up to 1000 times.
102    /// For UDP, this sets up the socket but doesn't establish a connection.
103    pub fn connect_to_server(&mut self, host: &str, port: u16) -> io::Result<()> {
104        let addr = format!("{}:{}", host, port);
105        self.server_addr = Some(addr.clone());
106
107        match self.protocol {
108            Protocol::Tcp => {
109                let addr = addr.to_socket_addrs()?.next().ok_or_else(|| {
110                    io::Error::new(io::ErrorKind::Other, "Failed to resolve address")
111                })?;
112
113                let mut last_error = None;
114                for _ in 0..1000 {
115                    match TcpStream::connect_timeout(&addr, self.timeout) {
116                        Ok(stream) => {
117                            // Set SO_LINGER
118                            self.set_linger(stream.as_raw_fd())?;
119
120                            stream.set_read_timeout(Some(self.timeout))?;
121                            stream.set_write_timeout(Some(self.timeout))?;
122                            self.connection = Some(Connection::Tcp(stream));
123                            return Ok(());
124                        }
125                        Err(e) => {
126                            last_error = Some(e);
127                            println!(
128                                "Failed to connect to {}: {}",
129                                addr,
130                                last_error.as_ref().unwrap()
131                            );
132                            sleep(Duration::from_micros(1000));
133                        }
134                    }
135                }
136
137                Err(last_error.unwrap_or_else(|| {
138                    io::Error::new(io::ErrorKind::Other, "Failed to connect after retries")
139                }))
140            }
141            Protocol::Udp => {
142                let socket = UdpSocket::bind("0.0.0.0:0")?;
143
144                // Set SO_LINGER
145                self.set_linger(socket.as_raw_fd())?;
146
147                socket.set_read_timeout(Some(self.timeout))?;
148                socket.set_write_timeout(Some(self.timeout))?;
149                self.connection = Some(Connection::Udp(socket));
150                Ok(())
151            }
152        }
153    }
154
155    /// Send a message to the connected server.
156    ///
157    /// # Arguments
158    /// * `message` - The byte array to send
159    ///
160    /// # Returns
161    /// `Ok(())` on successful send, or an error if sending fails
162    ///
163    /// # Description
164    /// For TCP, this writes directly to the stream.
165    /// For UDP, this sends a datagram to the previously specified server address.
166    pub fn send_message(&mut self, message: &[u8]) -> io::Result<()> {
167        match (&mut self.connection, self.server_addr.as_ref()) {
168            (Some(Connection::Tcp(stream)), _) => {
169                stream.write_all(message)?;
170            }
171            (Some(Connection::Udp(socket)), Some(addr)) => {
172                socket.send_to(message, addr)?;
173            }
174            _ => {
175                return Err(io::Error::new(
176                    io::ErrorKind::NotConnected,
177                    "No active connection",
178                ));
179            }
180        }
181        Ok(())
182    }
183
184    /// Receive a response from the server.
185    ///
186    /// # Returns
187    /// `Ok(Vec<u8>)` containing the received data on success, or an error if receiving fails
188    ///
189    /// # Description
190    /// Reads up to 2048 bytes from the connection and returns the actual data received.
191    #[allow(unused)]
192    fn receive_response(&mut self) -> io::Result<Vec<u8>> {
193        let mut buffer = vec![0; 2048];
194
195        let n = match &mut self.connection {
196            Some(Connection::Tcp(stream)) => stream.read(&mut buffer)?,
197            Some(Connection::Udp(socket)) => {
198                let (n, _) = socket.recv_from(&mut buffer)?;
199                n
200            }
201            None => {
202                return Err(io::Error::new(
203                    io::ErrorKind::NotConnected,
204                    "No active connection",
205                ));
206            }
207        };
208
209        buffer.truncate(n);
210        Ok(buffer)
211    }
212}
213
214/// Create a default network fuzzing harness.
215///
216/// # Arguments
217/// * `network` - Network client options containing connection details
218///
219/// # Returns
220/// A closure that takes a `BytesInput` and returns an `ExitKind`.
221///
222/// # Description
223/// The returned harness function will:
224/// 1. Connect to the server specified in the network options
225/// 2. Send the input data
226/// 3. Wait for the specified delay time
227/// 4. Return ExitKind::Ok
228pub fn create_default_harness<S, I>(
229    network: &NetworkClientOptions,
230) -> impl FnMut(&mut S, &I) -> ExitKind
231where
232    S: HasMetadata,
233    I: HasTargetBytes,
234{
235    let mut client = NetworkClient::new(network.protocol(), network.sleep);
236
237    move |_state: &mut S, input: &I| {
238        let mut do_send = false;
239        for _ in 0..network.try_time {
240            if let Err(_) = client.connect_to_server(&network.host, network.port) {
241                error!("Failed to connect to server");
242                sleep(Duration::from_secs(1));
243                continue;
244            }
245            if let Err(_) = client.send_message(input.target_bytes().as_slice()) {
246                error!("Failed to send message to server");
247                sleep(Duration::from_secs(1));
248                continue;
249            }
250            do_send = true;
251            break;
252        }
253        if !do_send {
254            panic!(
255                "Failed to send message to server in {} tries",
256                network.try_time
257            )
258        }
259        sleep(Duration::from_nanos(network.sleep));
260
261        ExitKind::Ok
262    }
263}