pub struct ServerAssociationOptions<'a, A> { /* private fields */ }Expand description
A DICOM association builder for an acceptor DICOM node, often taking the role of a service class provider (SCP).
This is the standard way of negotiating and establishing
an association with a requesting node.
The outcome is a ServerAssociation.
Unlike the ClientAssociationOptions,
a value of this type can be reused for multiple connections.
The SCP will by default accept all transfer syntaxes
supported by the main transfer syntax registry,
unless one or more transfer syntaxes are explicitly indicated
through calls to with_transfer_syntax.
Access control logic is also available, enabling application entities to decide on whether to accept or reject the association request based on the called and calling AE titles.
- By default, the application will accept requests from anyone
(
AcceptAny) - To only accept requests with a matching called AE title,
add a call to
accept_called_ae_title(AcceptCalledAeTitle). - Any other policy can be implemented through the
AccessControltrait.
§Basic Usage
§Synchronous API
Spawn a single sync thread to listen for incoming requests.
let scp_options = ServerAssociationOptions::new()
.with_abstract_syntax("1.2.840.10008.1.1")
.with_transfer_syntax("1.2.840.10008.1.2.1");
let (stream, _address) = tcp_listener.accept()?;
scp_options.establish(stream)?;§Asynchronous API
Spawn an async task for each incoming association request.
let listen_addr = SocketAddrV4::new(Ipv4Addr::from(0), 11111);
let listener = tokio::net::TcpListener::bind(listen_addr).await?;
loop {
let (socket, _addr) = listener.accept().await?;
tokio::task::spawn(async move {
let mut scp = ServerAssociationOptions::new()
.accept_any()
.with_abstract_syntax("1.2.840.10008.1.1")
.with_transfer_syntax("1.2.840.10008.1.2.1")
.establish_async(socket)
.await
.expect("Could not establish association on socket");
loop {
match scp.receive().await {
Ok(dicom_ul::Pdu::PData { data }) => {
// read P-Data here
},
Ok(dicom_ul::Pdu::ReleaseRP) => {
break;
},
Ok(dicom_ul::Pdu::AbortRQ { source }) => {
eprintln!("Association aborted: {source:?}");
break;
},
Ok(pdu) => {
eprintln!("Unexpected PDU");
},
Err(e) => {
eprintln!("Oops! {e}");
},
}
}
});
}
fn main() {}§TLS Support
Enabling one of the Cargo features sync-tls or async-tls
unlocks the methods for configuring TLS.
Call tls_config
for the server to expect associations established
over a secure transport connection.
§TLS in synchronous API
Include the sync-tls feature in your Cargo.toml.
§TLS in asynchronous API
Include the async-tls feature in your Cargo.toml.
§Example
use dicom_ul::{ServerAssociation, ServerAssociationOptions};
use std::net::TcpListener;
use rustls::{
ServerConfig, RootCertStore,
pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject},
server::WebPkiClientVerifier,
};
// Loading certificates and keys for demonstration purposes
let ca_cert = CertificateDer::from_pem_slice(std::fs::read("ssl/ca.crt")?.as_ref())
.expect("Failed to load client cert");
// Server certificate and private key -- signed by CA
let server_cert = CertificateDer::from_pem_slice(std::fs::read("ssl/server.crt")?.as_ref())
.expect("Failed to load server cert");
let server_private_key = PrivateKeyDer::from_pem_slice(std::fs::read("ssl/server.key")?.as_ref())
.expect("Failed to load client private key");
// Create a root cert store for the client which includes the server certificate
let mut certs = RootCertStore::empty();
certs.add_parsable_certificates(vec![ca_cert.clone()]);
// Server configuration.
// Creates a server config that requires client authentication (mutual TLS) using
// webpki for certificate verification.
let server_config = ServerConfig::builder()
.with_client_cert_verifier(
WebPkiClientVerifier::builder(certs.clone().into())
.build()
.expect("Failed to create client certificate verifier")
)
.with_single_cert(vec![server_cert.clone(), ca_cert.clone()], server_private_key)
.expect("Failed to create server TLS config");
let (stream, _address) = tcp_listener.accept()?;
let association: ServerAssociation<_> = ServerAssociationOptions::new()
.accept_called_ae_title()
.ae_title("TLS-SCP")
.with_abstract_syntax(dicom_dictionary_std::uids::VERIFICATION)
.tls_config(server_config)
.establish_tls(stream)?;For an association with the async API,
call establish_tls_async instead of establish_tls.
Implementations§
Source§impl ServerAssociationOptions<'_, AcceptAny>
impl ServerAssociationOptions<'_, AcceptAny>
Source§impl<'a, A> ServerAssociationOptions<'a, A>where
A: AccessControl,
impl<'a, A> ServerAssociationOptions<'a, A>where
A: AccessControl,
Sourcepub fn accept_any(self) -> ServerAssociationOptions<'a, AcceptAny>
pub fn accept_any(self) -> ServerAssociationOptions<'a, AcceptAny>
Change the access control policy to accept any association regardless of the specified AE titles.
This is the default behavior when the options are first created.
Sourcepub fn accept_called_ae_title(
self,
) -> ServerAssociationOptions<'a, AcceptCalledAeTitle>
pub fn accept_called_ae_title( self, ) -> ServerAssociationOptions<'a, AcceptCalledAeTitle>
Change the access control policy to accept an association if the called AE title matches this node’s AE title.
The default is to accept any requesting node regardless of the specified AE titles.
Sourcepub fn ae_access_control<P>(
self,
access_control: P,
) -> ServerAssociationOptions<'a, P>where
P: AccessControl,
pub fn ae_access_control<P>(
self,
access_control: P,
) -> ServerAssociationOptions<'a, P>where
P: AccessControl,
Change the access control policy.
The default is to accept any requesting node regardless of the specified AE titles.
Sourcepub fn ae_title<T>(self, ae_title: T) -> Self
pub fn ae_title<T>(self, ae_title: T) -> Self
Define the application entity title referring to this DICOM node.
The default is THIS-SCP.
Sourcepub fn with_abstract_syntax<T>(self, abstract_syntax_uid: T) -> Self
pub fn with_abstract_syntax<T>(self, abstract_syntax_uid: T) -> Self
Include this abstract syntax in the list of proposed presentation contexts.
Sourcepub fn with_transfer_syntax<T>(self, transfer_syntax_uid: T) -> Self
pub fn with_transfer_syntax<T>(self, transfer_syntax_uid: T) -> Self
Include this transfer syntax in each proposed presentation context.
Sourcepub fn max_pdu_length(self, value: u32) -> Self
pub fn max_pdu_length(self, value: u32) -> Self
Override the maximum expected PDU length.
Sourcepub fn strict(self, strict: bool) -> Self
pub fn strict(self, strict: bool) -> Self
Override strict mode: whether receiving PDUs must not surpass the negotiated maximum PDU length.
Sourcepub fn promiscuous(self, promiscuous: bool) -> Self
pub fn promiscuous(self, promiscuous: bool) -> Self
Override promiscuous mode: whether to accept unknown abstract syntaxes.
Sourcepub fn read_timeout(self, timeout: Duration) -> Self
pub fn read_timeout(self, timeout: Duration) -> Self
Set the read timeout for the underlying TCP socket
This is used to set both the read and write timeout.
Sourcepub fn write_timeout(self, timeout: Duration) -> Self
pub fn write_timeout(self, timeout: Duration) -> Self
Set the write timeout for the underlying TCP socket
Trait Implementations§
Source§impl<'a, A: Clone> Clone for ServerAssociationOptions<'a, A>
impl<'a, A: Clone> Clone for ServerAssociationOptions<'a, A>
Source§fn clone(&self) -> ServerAssociationOptions<'a, A>
fn clone(&self) -> ServerAssociationOptions<'a, A>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<'a, A: Debug> Debug for ServerAssociationOptions<'a, A>
impl<'a, A: Debug> Debug for ServerAssociationOptions<'a, A>
Auto Trait Implementations§
impl<'a, A> Freeze for ServerAssociationOptions<'a, A>where
A: Freeze,
impl<'a, A> RefUnwindSafe for ServerAssociationOptions<'a, A>where
A: RefUnwindSafe,
impl<'a, A> Send for ServerAssociationOptions<'a, A>where
A: Send,
impl<'a, A> Sync for ServerAssociationOptions<'a, A>where
A: Sync,
impl<'a, A> Unpin for ServerAssociationOptions<'a, A>where
A: Unpin,
impl<'a, A> UnsafeUnpin for ServerAssociationOptions<'a, A>where
A: UnsafeUnpin,
impl<'a, A> UnwindSafe for ServerAssociationOptions<'a, A>where
A: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more