tlspuffin/tcp/
mod.rs

1use std::ffi::OsStr;
2use std::io::{self, ErrorKind, Read, Write};
3use std::net::{AddrParseError, IpAddr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
4use std::path::Path;
5use std::process::{Child, Command, Stdio};
6use std::str::FromStr;
7use std::sync::mpsc::{self, channel};
8use std::thread;
9use std::time::Duration;
10
11use puffin::agent::{AgentDescriptor, AgentName};
12use puffin::algebra::ConcreteMessage;
13use puffin::claims::GlobalClaimList;
14use puffin::codec::Codec;
15use puffin::error::Error;
16use puffin::protocol::ProtocolBehavior;
17use puffin::put::{Put, PutOptions};
18use puffin::put_registry::{Factory, TCP_PUT};
19use puffin::stream::Stream;
20
21use crate::protocol::{AgentType, OpaqueMessageFlight, TLSDescriptorConfig, TLSProtocolBehavior};
22
23pub fn new_tcp_factory() -> Box<dyn Factory<TLSProtocolBehavior>> {
24    struct TCPFactory;
25    impl Factory<TLSProtocolBehavior> for TCPFactory {
26        fn create(
27            &self,
28            agent_descriptor: &AgentDescriptor<TLSDescriptorConfig>,
29            _claims: &GlobalClaimList<<TLSProtocolBehavior as ProtocolBehavior>::Claim>,
30            options: &PutOptions,
31        ) -> Result<Box<dyn Put<TLSProtocolBehavior>>, Error> {
32            if options.get_option("args").is_some() {
33                log::info!("Trace contains TCP running information we shall reuse.");
34                let args = options
35                    .get_option("args")
36                    .ok_or_else(|| {
37                        Error::Agent(format!("{} // {:?}", "Unable to find args", options))
38                    })?
39                    .to_owned();
40                let prog = options
41                    .get_option("prog")
42                    .ok_or_else(|| Error::Agent("Unable to find prog".to_string()))?
43                    .to_owned();
44                let cwd = options
45                    .get_option("cwd")
46                    .map(|cwd| Some(cwd.to_owned()))
47                    .unwrap_or_default();
48                if agent_descriptor.protocol_config.typ == AgentType::Client {
49                    let mut server = TcpServerPut::new(agent_descriptor, options)?;
50                    server.set_process(TLSProcess::new(&prog, &args, cwd.as_ref()));
51                    Ok(Box::new(server))
52                } else {
53                    let process = TLSProcess::new(&prog, &args, cwd);
54                    let mut client = TcpClientPut::new(agent_descriptor, options)?;
55                    client.set_process(process);
56                    Ok(Box::new(client))
57                }
58            } else {
59                log::info!("Trace contains no TCP running information so we fall back to external TCP client and servers.");
60                if agent_descriptor.protocol_config.typ == AgentType::Client {
61                    let server = TcpServerPut::new(agent_descriptor, options)?;
62                    Ok(Box::new(server))
63                } else {
64                    let client = TcpClientPut::new(agent_descriptor, options)?;
65                    Ok(Box::new(client))
66                }
67            }
68        }
69
70        fn name(&self) -> String {
71            String::from(TCP_PUT)
72        }
73
74        fn versions(&self) -> Vec<(String, String)> {
75            vec![(
76                "harness".to_string(),
77                format!("{} {}", TCP_PUT, puffin_build::puffin::full_version()),
78            )]
79        }
80
81        fn supports(&self, _capability: &str) -> bool {
82            false
83        }
84
85        fn clone_factory(&self) -> Box<dyn Factory<TLSProtocolBehavior>> {
86            Box::new(TCPFactory)
87        }
88
89        fn rng_reseed(&self) {
90            log::debug!("[RNG] reseed failed ({}): not supported", self.name());
91        }
92    }
93
94    Box::new(TCPFactory)
95}
96
97trait TcpPut {
98    fn write_to_stream(&mut self, buf: &[u8]) -> io::Result<()>;
99
100    fn read_to_flight(&mut self) -> Result<Option<OpaqueMessageFlight>, Error>;
101}
102
103/// A PUT which is backed by a TCP stream to a server.
104/// In order to use this start an OpenSSL server like this:
105///
106/// ```bash
107/// openssl s_server -key key.pem -cert cert.pem -accept 44330 -msg -debug -state
108/// ```
109pub struct TcpClientPut {
110    stream: TcpStream,
111    agent_descriptor: AgentDescriptor<TLSDescriptorConfig>,
112    process: Option<TLSProcess>,
113}
114
115impl TcpPut for TcpClientPut {
116    fn write_to_stream(&mut self, buf: &[u8]) -> io::Result<()> {
117        self.stream.write_all(buf)?;
118        self.stream.flush()
119    }
120
121    fn read_to_flight(&mut self) -> Result<Option<OpaqueMessageFlight>, Error> {
122        let mut buf = vec![];
123        let _ = self.stream.read_to_end(&mut buf);
124        let flight = OpaqueMessageFlight::read_bytes(&buf);
125        Ok(flight)
126    }
127}
128
129impl TcpClientPut {
130    fn new(
131        agent_descriptor: &AgentDescriptor<TLSDescriptorConfig>,
132        options: &PutOptions,
133    ) -> Result<Self, Error> {
134        let addr = addr_from_config(options).map_err(|err| Error::Put(err.to_string()))?;
135        let stream = Self::new_stream(addr)?;
136
137        Ok(Self {
138            stream,
139            agent_descriptor: agent_descriptor.clone(),
140            process: None,
141        })
142    }
143
144    fn new_stream<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
145        let mut tries = 500;
146        let stream = loop {
147            if let Ok(stream) = TcpStream::connect(&addr) {
148                // We are waiting 500ms for a response of the PUT behind the TCP socket.
149                // If we are expecting data from it and this timeout is reached, then we assume that
150                // no more will follow.
151                stream.set_read_timeout(Some(Duration::from_millis(500)))?;
152                stream.set_nodelay(true)?;
153                break Some(stream);
154            }
155
156            tries -= 1;
157
158            if tries == 0 {
159                break None;
160            }
161
162            thread::sleep(Duration::from_millis(100));
163        };
164
165        stream.ok_or(io::Error::new(
166            ErrorKind::NotConnected,
167            "TcpClientPut failed to connect",
168        ))
169    }
170
171    pub fn set_process(&mut self, process: TLSProcess) {
172        self.process = Some(process)
173    }
174}
175
176pub struct TcpServerPut {
177    stream: Option<(TcpStream, TcpListener)>,
178    stream_receiver: mpsc::Receiver<(TcpStream, TcpListener)>,
179    agent_descriptor: AgentDescriptor<TLSDescriptorConfig>,
180    process: Option<TLSProcess>,
181}
182
183impl TcpServerPut {
184    fn new(
185        agent_descriptor: &AgentDescriptor<TLSDescriptorConfig>,
186        options: &PutOptions,
187    ) -> Result<Self, Error> {
188        let (sender, stream_receiver) = channel();
189        let addr = addr_from_config(options).map_err(|err| Error::Put(err.to_string()))?;
190
191        let listener = TcpListener::bind(addr).unwrap();
192
193        thread::spawn(move || {
194            if let Some(new_stream) = listener.incoming().next() {
195                let stream = new_stream.unwrap();
196                // We are waiting 500ms for a response of the PUT behind the TCP socket.
197                // If we are expecting data from it and this timeout is reached, then we assume that
198                // no more will follow.
199                stream
200                    .set_read_timeout(Some(Duration::from_millis(500)))
201                    .unwrap();
202                stream.set_nodelay(true).unwrap();
203                sender.send((stream, listener)).unwrap();
204            }
205        });
206
207        Ok(Self {
208            stream: None,
209            stream_receiver,
210            agent_descriptor: agent_descriptor.clone(),
211            process: None,
212        })
213    }
214
215    pub fn set_process(&mut self, process: TLSProcess) {
216        self.process = Some(process)
217    }
218
219    pub fn receive_stream(&mut self) {
220        if self.stream.is_some() {
221            return;
222        }
223
224        if let Ok(tuple) = self.stream_receiver.recv_timeout(Duration::from_secs(60)) {
225            self.stream = Some(tuple);
226        } else {
227            panic!("Unable to get stream to client!")
228        }
229    }
230}
231
232impl TcpPut for TcpServerPut {
233    fn write_to_stream(&mut self, buf: &[u8]) -> io::Result<()> {
234        self.receive_stream();
235        let stream = &mut self.stream.as_mut().unwrap().0;
236        stream.write_all(buf)?;
237        stream.flush()?;
238        Ok(())
239    }
240
241    fn read_to_flight(&mut self) -> Result<Option<OpaqueMessageFlight>, Error> {
242        self.receive_stream();
243        let mut buf = vec![];
244        let _ = self.stream.as_mut().unwrap().0.read_to_end(&mut buf);
245        let flight = OpaqueMessageFlight::read_bytes(&buf);
246        Ok(flight)
247    }
248}
249
250impl Stream<TLSProtocolBehavior> for TcpServerPut {
251    fn add_to_inbound(&mut self, message: &ConcreteMessage) {
252        self.write_to_stream(message).unwrap();
253    }
254
255    fn take_message_from_outbound(
256        &mut self,
257        output_flight: &mut Option<OpaqueMessageFlight>,
258    ) -> Result<(), Error> {
259        take_message_from_outbound(self, output_flight)
260    }
261}
262
263impl Stream<TLSProtocolBehavior> for TcpClientPut {
264    fn add_to_inbound(&mut self, message: &ConcreteMessage) {
265        self.write_to_stream(message).unwrap();
266    }
267
268    fn take_message_from_outbound(
269        &mut self,
270        output_flight: &mut Option<OpaqueMessageFlight>,
271    ) -> Result<(), Error> {
272        take_message_from_outbound(self, output_flight)
273    }
274}
275
276fn take_message_from_outbound<P: TcpPut>(
277    put: &mut P,
278    output_flight: &mut Option<OpaqueMessageFlight>,
279) -> Result<(), Error> {
280    *output_flight = put.read_to_flight()?;
281    Ok(())
282}
283
284fn addr_from_config(options: &PutOptions) -> Result<SocketAddr, AddrParseError> {
285    let host = options.get_option("host").unwrap_or("127.0.0.1");
286    let port = options
287        .get_option("port")
288        .and_then(|value| u16::from_str(value).ok())
289        .unwrap_or_else(|| {
290            let port = 44338;
291            log::warn!(
292                "Failed to parse port option (maybe you executed a trace that was not produced in \
293            TCP mode?). We anyway fall back to port {port}."
294            );
295            port
296        });
297
298    Ok(SocketAddr::new(IpAddr::from_str(host)?, port))
299}
300
301impl Put<TLSProtocolBehavior> for TcpServerPut {
302    fn progress(&mut self) -> Result<(), Error> {
303        Ok(())
304    }
305
306    fn reset(&mut self, _new_name: AgentName) -> Result<(), Error> {
307        panic!("Not supported")
308    }
309
310    fn descriptor(&self) -> &AgentDescriptor<TLSDescriptorConfig> {
311        &self.agent_descriptor
312    }
313
314    fn describe_state(&self) -> String {
315        panic!("Not supported")
316    }
317
318    fn is_state_successful(&self) -> bool {
319        false
320    }
321
322    fn shutdown(&mut self) -> String {
323        self.process.as_mut().unwrap().shutdown().unwrap()
324    }
325
326    fn version() -> String
327    where
328        Self: Sized,
329    {
330        "Undefined".to_string()
331    }
332}
333
334impl Put<TLSProtocolBehavior> for TcpClientPut {
335    fn progress(&mut self) -> Result<(), Error> {
336        Ok(())
337    }
338
339    fn reset(&mut self, new_name: AgentName) -> Result<(), Error> {
340        self.agent_descriptor.name = new_name;
341        let address = self.stream.peer_addr()?;
342        self.stream = Self::new_stream(address)?;
343        Ok(())
344    }
345
346    fn descriptor(&self) -> &AgentDescriptor<TLSDescriptorConfig> {
347        &self.agent_descriptor
348    }
349
350    fn describe_state(&self) -> String {
351        panic!("Not supported")
352    }
353
354    fn is_state_successful(&self) -> bool {
355        false
356    }
357
358    fn shutdown(&mut self) -> String {
359        self.process.as_mut().unwrap().shutdown().unwrap()
360    }
361
362    fn version() -> String
363    where
364        Self: Sized,
365    {
366        "Undefined".to_string()
367    }
368}
369
370pub struct TLSProcess {
371    child: Option<Child>,
372    output: Option<String>,
373}
374
375impl TLSProcess {
376    pub fn new<P: AsRef<Path>>(prog: &str, args: &str, cwd: Option<P>) -> Self {
377        Self {
378            child: Some(execute_command(prog, args.split(' '), cwd)),
379            output: None,
380        }
381    }
382
383    pub fn shutdown(&mut self) -> Option<String> {
384        self.output = if let Some(mut child) = self.child.take() {
385            child.kill().expect("failed to stop process");
386
387            Some(collect_output(child))
388        } else {
389            None
390        };
391
392        self.output.clone()
393    }
394}
395
396impl Drop for TLSProcess {
397    fn drop(&mut self) {
398        if let Some(output) = self.shutdown() {
399            println!(
400                "TLSProcess was not shutdown manually. Output of the process: {}",
401                output
402            );
403        }
404    }
405}
406
407pub fn collect_output(child: Child) -> String {
408    let output = child.wait_with_output().expect("failed to wait on child");
409    let mut complete = "--- start stderr\n".to_string();
410
411    complete.push_str(std::str::from_utf8(&output.stderr).unwrap());
412    complete.push_str("\n--- end stderr\n");
413    complete.push_str("--- start stdout\n");
414    complete.push_str(std::str::from_utf8(&output.stdout).unwrap());
415    complete.push_str("\n--- end stdout\n");
416
417    complete
418}
419
420pub fn execute_command<I, S, P: AsRef<Path>>(prog: &str, args: I, cwd: Option<P>) -> Child
421where
422    I: IntoIterator<Item = S>,
423    S: AsRef<OsStr>,
424{
425    let mut cmd = Command::new(prog);
426
427    if let Some(cwd) = cwd {
428        cmd.current_dir(cwd);
429    }
430
431    cmd.args(args)
432        .stdin(Stdio::piped()) // This line is super important! Else the OpenSSL server immediately stops
433        .stdout(Stdio::piped())
434        .stderr(Stdio::piped())
435        .spawn()
436        .expect("failed to execute process")
437}
438
439#[cfg(test)]
440mod tests {
441    use puffin::execution::{Runner, TraceRunner};
442    use puffin::put::PutDescriptor;
443    use puffin::put_registry::TCP_PUT;
444    use puffin::trace::Spawner;
445
446    use crate::protocol::TLSVersion;
447    #[allow(unused_imports)]
448    use crate::{test_utils::prelude::*, tls::seeds::*};
449
450    #[test_log::test]
451    fn test_openssl_session_resumption_dhe_full() {
452        let port = 44330;
453        let guard = openssl_server(port, TLSVersion::V1_3);
454        let trace = seed_session_resumption_dhe_full.build_trace();
455        let server = trace.descriptors[0].name;
456        let runner = default_runner_for_desc(PutDescriptor::new(TCP_PUT, guard.build_options()));
457
458        let mut context = runner.execute(trace, &mut 0).unwrap();
459
460        let shutdown = context.find_agent_mut(server).unwrap().shutdown();
461        log::info!("{}", shutdown);
462        assert!(shutdown.contains("Reused session-id"));
463    }
464
465    #[test_log::test]
466    fn test_openssl_seed_client_attacker_full() {
467        let port = 44331;
468        let guard = openssl_server(port, TLSVersion::V1_3);
469        let runner = default_runner_for_desc(PutDescriptor::new(TCP_PUT, guard.build_options()));
470        let trace = seed_client_attacker_full.build_trace();
471        let server = trace.descriptors[0].name;
472
473        let mut context = runner.execute(trace, &mut 0).unwrap();
474
475        let shutdown = context.find_agent_mut(server).unwrap().shutdown();
476        log::info!("{}", shutdown);
477        assert!(shutdown.contains("BEGIN SSL SESSION PARAMETERS"));
478        assert!(!shutdown.contains("Reused session-id"));
479    }
480
481    #[test_log::test]
482    fn test_openssl_openssl_seed_successful12() {
483        let trace = seed_successful12_with_tickets.build_trace();
484
485        let server_port = 44332;
486        let server_agent = trace.descriptors[1].name;
487        let server_guard = openssl_server(server_port, TLSVersion::V1_2);
488        let server = PutDescriptor::new(TCP_PUT, server_guard.build_options());
489
490        let client_port = 44333;
491        let client_agent = trace.descriptors[0].name;
492        let client_guard = openssl_client(client_port, TLSVersion::V1_2);
493        let client = PutDescriptor::new(TCP_PUT, client_guard.build_options());
494
495        let put_registry = tls_registry();
496        let runner = Runner::new(
497            put_registry.clone(),
498            Spawner::new(put_registry)
499                .with_mapping(&[(client_agent, client), (server_agent, server)]),
500        );
501
502        let mut context = runner.execute(trace, &mut 0).unwrap();
503
504        let shutdown = context.find_agent_mut(client_agent).unwrap().shutdown();
505        log::info!("{}", shutdown);
506        assert!(shutdown.contains("Timeout   : 7200 (sec)"));
507
508        let shutdown = context.find_agent_mut(server_agent).unwrap().shutdown();
509        log::info!("{}", shutdown);
510        assert!(shutdown.contains("BEGIN SSL SESSION PARAMETERS"));
511    }
512
513    #[test_log::test]
514    #[ignore] // wolfssl example server and client are not available in CI
515    fn test_wolfssl_openssl_seed_successful12() {
516        let trace = seed_successful12_with_tickets.build_trace();
517
518        let server_port = 44334;
519        let server_agent = trace.descriptors[1].name;
520        let server_guard = openssl_server(server_port, TLSVersion::V1_2);
521        let server = PutDescriptor::new(TCP_PUT, server_guard.build_options());
522
523        let client_port = 44335;
524        let client_agent = trace.descriptors[0].name;
525        let client_guard = wolfssl_client(client_port, TLSVersion::V1_2, None);
526        let client = PutDescriptor::new(TCP_PUT, client_guard.build_options());
527
528        let put_registry = tls_registry();
529        let runner = Runner::new(
530            put_registry.clone(),
531            Spawner::new(put_registry)
532                .with_mapping(&[(client_agent, client), (server_agent, server)]),
533        );
534
535        let mut context = runner.execute(trace, &mut 0).unwrap();
536
537        let shutdown = context.find_agent_mut(client_agent).unwrap().shutdown();
538        log::info!("{}", shutdown);
539        assert!(!shutdown.contains("fail"));
540
541        let shutdown = context.find_agent_mut(server_agent).unwrap().shutdown();
542        log::info!("{}", shutdown);
543        assert!(shutdown.contains("BEGIN SSL SESSION PARAMETERS"));
544    }
545}