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}