Skip to main content

rusty_copp/
lib.rs

1pub(crate) mod api;
2pub(crate) mod error;
3pub(crate) mod messages;
4pub(crate) mod service;
5use rusty_cosp::{RustyCospInitiatorIsoStack, RustyCospReaderIsoStack, RustyCospResponderIsoStack, RustyCospWriterIsoStack};
6
7pub use api::*;
8pub use service::*;
9
10pub type RustyCoppReaderIsoStack<R> = RustyCoppReader<RustyCospReaderIsoStack<R>>;
11pub type RustyCoppWriterIsoStack<W> = RustyCoppWriter<RustyCospWriterIsoStack<W>>;
12pub type RustyCoppInitiatorIsoStack<R, W> = RustyCoppInitiator<RustyCospInitiatorIsoStack<R, W>, RustyCospReaderIsoStack<R>, RustyCospWriterIsoStack<W>>;
13pub type RustyCoppListenerIsoStack<R, W> = RustyCoppListener<RustyCospResponderIsoStack<R, W>, RustyCospReaderIsoStack<R>, RustyCospWriterIsoStack<W>>;
14pub type RustyCoppResponderIsoStack<R, W> = RustyCoppResponder<RustyCospResponderIsoStack<R, W>, RustyCospReaderIsoStack<R>, RustyCospWriterIsoStack<W>>;
15pub type RustyCoppConnectionIsoStack<R, W> = RustyCoppConnection<RustyCospReaderIsoStack<R>, RustyCospWriterIsoStack<W>>;
16
17#[cfg(test)]
18mod tests {
19    use std::{time::Duration, vec};
20
21    use der_parser::Oid;
22    use rusty_cosp::{TcpCospInitiator, TcpCospListener, TcpCospReader, TcpCospResponder, TcpCospWriter};
23    use rusty_cotp::{CotpAcceptInformation, CotpConnectInformation, CotpResponder, TcpCotpAcceptor, TcpCotpConnection, TcpCotpReader, TcpCotpWriter};
24    use rusty_tpkt::{TcpTpktConnection, TcpTpktReader, TcpTpktServer, TcpTpktWriter};
25    use tokio::join;
26    use tracing_test::traced_test;
27
28    use super::*;
29
30    #[tokio::test]
31    #[traced_test]
32    async fn it_should_create_connection() -> Result<(), anyhow::Error> {
33        let options = CoppConnectionInformation {
34            calling_presentation_selector: Some(vec![0x00, 0x00, 0x00, 0x23]),
35            called_presentation_selector: Some(vec![0x65, 0x00, 0x00, 0x00]),
36            ..Default::default()
37        };
38        let presentation_contexts = vec![
39            // ACSE
40            PresentationContext {
41                indentifier: vec![1],
42                abstract_syntax_name: Oid::from(&[2, 2, 1, 0, 1]).map_err(|e| CoppError::InternalError(e.to_string()))?,
43                transfer_syntax_name_list: vec![Oid::from(&[2, 1, 1]).map_err(|e| CoppError::InternalError(e.to_string()))?],
44            },
45            // MMS
46            PresentationContext {
47                indentifier: vec![3],
48                abstract_syntax_name: Oid::from(&[1, 0, 9506, 2, 1]).map_err(|e| CoppError::InternalError(e.to_string()))?,
49                transfer_syntax_name_list: vec![Oid::from(&[2, 1, 1]).map_err(|e| CoppError::InternalError(e.to_string()))?],
50            },
51        ];
52        let (client_connection, server_connection) = create_copp_connection_pair_with_options(
53            Some(UserData::FullyEncoded(vec![PresentationDataValueList {
54                presentation_context_identifier: vec![0x01],
55                presentation_data_values: PresentationDataValues::SingleAsn1Type(vec![0x60, 0x09, 0xa1, 0x07, 0x06, 0x05, 0x28, 0xca, 0x22, 0x02, 0x03]),
56                transfer_syntax_name: None,
57            }])),
58            options,
59            Some(UserData::FullyEncoded(vec![PresentationDataValueList {
60                presentation_context_identifier: vec![0x01],
61                presentation_data_values: PresentationDataValues::SingleAsn1Type(vec![0x61, 0x09, 0xa1, 0x07, 0x06, 0x05, 0x28, 0xca, 0x22, 0x02, 0x03]),
62                transfer_syntax_name: None,
63            }])),
64            presentation_contexts,
65        )
66        .await?;
67
68        let (mut client_reader, mut client_writer) = client_connection.split().await?;
69        let (mut server_reader, mut server_writer) = server_connection.split().await?;
70
71        client_writer
72            .send(&UserData::FullyEncoded(vec![PresentationDataValueList {
73                presentation_context_identifier: vec![0x03],
74                presentation_data_values: PresentationDataValues::SingleAsn1Type(vec![0x60, 0x09, 0xa1, 0x07, 0x06, 0x05, 0x28, 0xca, 0x22, 0x02, 0x03]),
75                transfer_syntax_name: None,
76            }]))
77            .await?;
78        server_reader.recv().await?;
79        server_writer
80            .send(&UserData::FullyEncoded(vec![PresentationDataValueList {
81                presentation_context_identifier: vec![0x03],
82                presentation_data_values: PresentationDataValues::SingleAsn1Type(vec![0x60, 0x09, 0xa1, 0x07, 0x06, 0x05, 0x28, 0xca, 0x22, 0x02, 0x03]),
83                transfer_syntax_name: None,
84            }]))
85            .await?;
86        client_reader.recv().await?;
87
88        Ok(())
89    }
90
91    async fn create_copp_connection_pair_with_options(
92        connect_data: Option<UserData>,
93        options: CoppConnectionInformation,
94        accept_data: Option<UserData>,
95        contexts: Vec<PresentationContext>,
96    ) -> Result<(impl CoppConnection, impl CoppConnection), anyhow::Error> {
97        // let test_address = format!("127.0.0.1:{}", rand::random_range::<u16, Range<u16>>(20000..30000)).parse()?;
98        let test_address = "127.0.0.1:10002".parse()?;
99
100        let connect_information = CotpConnectInformation::default();
101
102        let client_path = async {
103            tokio::time::sleep(Duration::from_millis(1)).await; // Give the server time to start
104            let tpkt_client = TcpTpktConnection::connect(test_address).await?;
105            let cotp_client = TcpCotpConnection::<TcpTpktReader, TcpTpktWriter>::initiate(tpkt_client, connect_information.clone()).await?;
106            let cosp_client = TcpCospInitiator::<TcpCotpReader<TcpTpktReader>, TcpCotpWriter<TcpTpktWriter>>::new(cotp_client, Default::default()).await?;
107            let copp_client =
108                RustyCoppInitiator::<TcpCospInitiator<TcpCotpReader<TcpTpktReader>, TcpCotpWriter<TcpTpktWriter>>, TcpCospReader<TcpCotpReader<TcpTpktReader>>, TcpCospWriter<TcpCotpWriter<TcpTpktWriter>>>::new(cosp_client, options);
109            Ok(copp_client.initiate(PresentationContextType::ContextDefinitionList(contexts), connect_data.clone()).await?)
110        };
111        let server_path = async {
112            let tpkt_server = TcpTpktServer::listen(test_address).await?;
113            let (tpkt_connection, _) = tpkt_server.accept().await?;
114            let (cotp_server, _) = TcpCotpAcceptor::<TcpTpktReader, TcpTpktWriter>::new(tpkt_connection).await?;
115            let cotp_connection = cotp_server.accept(CotpAcceptInformation::default()).await?;
116            let (cosp_listener, _) = TcpCospListener::<TcpCotpReader<TcpTpktReader>, TcpCotpWriter<TcpTpktWriter>>::new(cotp_connection).await?;
117            let (copp_listener, _) =
118                RustyCoppListener::<TcpCospResponder<TcpCotpReader<TcpTpktReader>, TcpCotpWriter<TcpTpktWriter>>, TcpCospReader<TcpCotpReader<TcpTpktReader>>, TcpCospWriter<TcpCotpWriter<TcpTpktWriter>>>::new(cosp_listener).await?;
119            let (copp_responder, connect_user_data) = copp_listener.responder().await?;
120
121            Ok((copp_responder.accept(accept_data.clone()).await?, connect_user_data))
122        };
123
124        let (copp_client, copp_server): (Result<_, anyhow::Error>, Result<_, anyhow::Error>) = join!(client_path, server_path);
125        let (copp_client, accepted_data) = copp_client?;
126        let (copp_server, connected_data) = copp_server?;
127
128        assert_eq!(accept_data, accepted_data);
129        assert_eq!(connect_data, connected_data);
130
131        Ok((copp_client, copp_server))
132    }
133}