Skip to main content

dicom_ul/association/
server.rs

1//! Association acceptor module
2//!
3//! The module provides an abstraction for a DICOM association
4//! in which this application entity listens to incoming association requests.
5//! See [`ServerAssociationOptions`]
6//! for details and examples on how to create an association.
7use bytes::BytesMut;
8use std::borrow::Cow;
9use std::time::Duration;
10use std::{io::Write, net::TcpStream};
11
12use crate::association::private::SyncAssociationSealed;
13use crate::association::{
14    encode_pdu, read_pdu_from_wire, AbortedSnafu, Association, CloseSocket,
15    MissingAbstractSyntaxSnafu, RejectedSnafu, SendPduSnafu, SocketOptions, SyncAssociation,
16    UnexpectedPduSnafu, UnknownPduSnafu, WireSendSnafu,
17};
18use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
19use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
20use snafu::{ensure, ResultExt};
21
22use crate::association::NegotiatedOptions;
23use crate::pdu::{PresentationContextNegotiated, LARGE_PDU_SIZE};
24use crate::{
25    pdu::{
26        write_pdu, AbortRQServiceProviderReason, AbortRQSource, AssociationAC, AssociationRJ,
27        AssociationRJResult, AssociationRJServiceUserReason, AssociationRJSource, AssociationRQ,
28        Pdu, PresentationContextResult, PresentationContextResultReason, UserIdentity,
29        UserVariableItem, DEFAULT_MAX_PDU, PDU_HEADER_SIZE,
30    },
31    IMPLEMENTATION_CLASS_UID, IMPLEMENTATION_VERSION_NAME,
32};
33
34use super::{uid::trim_uid, Error, Result};
35
36#[cfg(feature = "async")]
37use crate::association::AsyncAssociation;
38
39// stray module from 0.9.0, remove in 0.10.0
40#[deprecated(since = "0.9.1")]
41pub mod non_blocking {}
42
43#[cfg(feature = "sync-tls")]
44pub type TlsStream = rustls::StreamOwned<rustls::ServerConnection, std::net::TcpStream>;
45#[cfg(feature = "async-tls")]
46pub type AsyncTlsStream = tokio_rustls::server::TlsStream<tokio::net::TcpStream>;
47
48/// Common interface for application entity access control policies.
49///
50/// Existing implementations include [`AcceptAny`] and [`AcceptCalledAeTitle`],
51/// but users are free to implement their own.
52pub trait AccessControl {
53    /// Obtain the decision of whether to accept an incoming association request
54    /// based on the recorded application entity titles and/or user identity.
55    ///
56    /// Returns Ok(()) if the requester node should be given clearance.
57    /// Otherwise, a concrete association RJ service user reason is given.
58    fn check_access(
59        &self,
60        this_ae_title: &str,
61        calling_ae_title: &str,
62        called_ae_title: &str,
63        user_identity: Option<&UserIdentity>,
64    ) -> Result<(), AssociationRJServiceUserReason>;
65}
66
67/// An access control rule that accepts any incoming association request.
68#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
69pub struct AcceptAny;
70
71impl AccessControl for AcceptAny {
72    fn check_access(
73        &self,
74        _this_ae_title: &str,
75        _calling_ae_title: &str,
76        _called_ae_title: &str,
77        _user_identity: Option<&UserIdentity>,
78    ) -> Result<(), AssociationRJServiceUserReason> {
79        Ok(())
80    }
81}
82
83/// An access control rule that accepts association requests
84/// that match the called AE title with the node's AE title.
85#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
86pub struct AcceptCalledAeTitle;
87
88impl AccessControl for AcceptCalledAeTitle {
89    fn check_access(
90        &self,
91        this_ae_title: &str,
92        _calling_ae_title: &str,
93        called_ae_title: &str,
94        _user_identity: Option<&UserIdentity>,
95    ) -> Result<(), AssociationRJServiceUserReason> {
96        if this_ae_title == called_ae_title {
97            Ok(())
98        } else {
99            Err(AssociationRJServiceUserReason::CalledAETitleNotRecognized)
100        }
101    }
102}
103
104/// A DICOM association builder for an acceptor DICOM node,
105/// often taking the role of a service class provider (SCP).
106///
107/// This is the standard way of negotiating and establishing
108/// an association with a requesting node.
109/// The outcome is a [`ServerAssociation`].
110/// Unlike the [`ClientAssociationOptions`],
111/// a value of this type can be reused for multiple connections.
112///
113/// [`ClientAssociationOptions`]: crate::association::ClientAssociationOptions
114///
115/// The SCP will by default accept all transfer syntaxes
116/// supported by the main [transfer syntax registry][1],
117/// unless one or more transfer syntaxes are explicitly indicated
118/// through calls to [`with_transfer_syntax`][2].
119///
120/// Access control logic is also available,
121/// enabling application entities to decide on
122/// whether to accept or reject the association request
123/// based on the _called_ and _calling_ AE titles.
124///
125/// - By default, the application will accept requests from anyone
126///   ([`AcceptAny`])
127/// - To only accept requests with a matching _called_ AE title,
128///   add a call to [`accept_called_ae_title`]
129///   ([`AcceptCalledAeTitle`]).
130/// - Any other policy can be implemented through the [`AccessControl`] trait.
131///
132/// [`accept_called_ae_title`]: Self::accept_called_ae_title
133/// [`AcceptAny`]: AcceptAny
134/// [`AcceptCalledAeTitle`]: AcceptCalledAeTitle
135/// [`AccessControl`]: AccessControl
136///
137/// [1]: dicom_transfer_syntax_registry
138/// [2]: ServerAssociationOptions::with_transfer_syntax
139///
140/// ## Basic Usage
141///
142/// ### Synchronous API
143///
144/// Spawn a single sync thread to listen for incoming requests.
145/// ```no_run
146/// # use std::net::TcpListener;
147/// # use dicom_ul::association::server::ServerAssociationOptions;
148/// # fn run() -> Result<(), Box<dyn std::error::Error>> {
149/// # let tcp_listener: TcpListener = unimplemented!();
150/// let scp_options = ServerAssociationOptions::new()
151///    .with_abstract_syntax("1.2.840.10008.1.1")
152///    .with_transfer_syntax("1.2.840.10008.1.2.1");
153///
154/// let (stream, _address) = tcp_listener.accept()?;
155/// scp_options.establish(stream)?;
156/// # Ok(())
157/// # }
158/// ```
159///
160/// ### Asynchronous API
161///
162/// Spawn an async task for each incoming association request.
163///
164/// ```no_run
165/// # use std::net::{Ipv4Addr, SocketAddrV4};
166/// # use dicom_ul::association::{server::ServerAssociationOptions};
167/// # #[cfg(feature = "async")]
168/// # #[tokio::main]
169/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
170/// # use dicom_ul::association::AsyncAssociation;
171/// let listen_addr = SocketAddrV4::new(Ipv4Addr::from(0), 11111);
172/// let listener = tokio::net::TcpListener::bind(listen_addr).await?;
173/// loop {
174///     let (socket, _addr) = listener.accept().await?;
175///     tokio::task::spawn(async move {
176///         let mut scp = ServerAssociationOptions::new()
177///             .accept_any()
178///             .with_abstract_syntax("1.2.840.10008.1.1")
179///             .with_transfer_syntax("1.2.840.10008.1.2.1")
180///             .establish_async(socket)
181///             .await
182///             .expect("Could not establish association on socket");
183///         loop {
184///             match scp.receive().await {
185///                 Ok(dicom_ul::Pdu::PData { data }) => {
186///                     // read P-Data here
187///                 },
188///                 Ok(dicom_ul::Pdu::ReleaseRP) => {
189///                     break;
190///                 },
191///                 Ok(dicom_ul::Pdu::AbortRQ { source }) => {
192///                     eprintln!("Association aborted: {source:?}");
193///                     break;
194///                 },
195///                 Ok(pdu) => {
196///                     eprintln!("Unexpected PDU");
197///                 },
198///                 Err(e) => {
199///                     eprintln!("Oops! {e}");
200///                 },
201///             }
202///         }
203///     });
204/// }
205/// # Ok(())
206/// # }
207/// # #[cfg(not(feature = "async"))]
208/// fn main() {}
209/// ```
210///
211/// ## TLS Support
212///
213/// Enabling one of the Cargo features `sync-tls` or `async-tls`
214/// unlocks the methods for configuring TLS.
215/// Call `tls_config`
216/// for the server to expect associations established
217/// over a secure transport connection.
218///
219/// #### TLS in synchronous API
220///
221/// Include the `sync-tls` feature in your `Cargo.toml`.
222///
223/// #### TLS in asynchronous API
224///
225/// Include the `async-tls` feature in your `Cargo.toml`.
226///
227/// ### Example
228///
229/// ```no_run
230/// # use std::time::Duration;
231/// # use std::sync::Arc;
232/// # #[cfg(feature = "sync-tls")]
233/// # fn run() -> Result<(), Box<dyn std::error::Error>> {
234/// use dicom_ul::{ServerAssociation, ServerAssociationOptions};
235/// use std::net::TcpListener;
236/// use rustls::{
237///     ServerConfig, RootCertStore,
238///     pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject},
239///     server::WebPkiClientVerifier,
240/// };
241/// # let tcp_listener: TcpListener = unimplemented!();
242/// // Loading certificates and keys for demonstration purposes
243/// let ca_cert = CertificateDer::from_pem_slice(std::fs::read("ssl/ca.crt")?.as_ref())
244///     .expect("Failed to load client cert");
245///
246/// // Server certificate and private key -- signed by CA
247/// let server_cert = CertificateDer::from_pem_slice(std::fs::read("ssl/server.crt")?.as_ref())
248///     .expect("Failed to load server cert");
249///
250/// let server_private_key = PrivateKeyDer::from_pem_slice(std::fs::read("ssl/server.key")?.as_ref())
251///     .expect("Failed to load client private key");
252///
253/// // Create a root cert store for the client which includes the server certificate
254/// let mut certs = RootCertStore::empty();
255/// certs.add_parsable_certificates(vec![ca_cert.clone()]);
256///
257/// // Server configuration.
258/// // Creates a server config that requires client authentication (mutual TLS) using
259/// // webpki for certificate verification.
260/// let server_config = ServerConfig::builder()
261///     .with_client_cert_verifier(
262///         WebPkiClientVerifier::builder(certs.clone().into())
263///             .build()
264///             .expect("Failed to create client certificate verifier")
265///     )
266///     .with_single_cert(vec![server_cert.clone(), ca_cert.clone()], server_private_key)
267///     .expect("Failed to create server TLS config");
268///
269/// let (stream, _address) = tcp_listener.accept()?;
270///
271/// let association: ServerAssociation<_> = ServerAssociationOptions::new()
272///     .accept_called_ae_title()
273///     .ae_title("TLS-SCP")
274///     .with_abstract_syntax(dicom_dictionary_std::uids::VERIFICATION)
275///     .tls_config(server_config)
276///     .establish_tls(stream)?;
277/// # Ok(())
278/// # }
279/// ```
280///
281/// For an association with the async API,
282/// call `establish_tls_async` instead of `establish_tls`.
283#[derive(Debug, Clone)]
284pub struct ServerAssociationOptions<'a, A> {
285    /// the application entity access control policy
286    ae_access_control: A,
287    /// the AE title of this DICOM node
288    ae_title: Cow<'a, str>,
289    /// the requested application context name
290    application_context_name: Cow<'a, str>,
291    /// the list of requested abstract syntaxes
292    abstract_syntax_uids: Vec<Cow<'a, str>>,
293    /// the list of requested transfer syntaxes
294    transfer_syntax_uids: Vec<Cow<'a, str>>,
295    /// the expected protocol version
296    protocol_version: u16,
297    /// the maximum PDU length
298    max_pdu_length: u32,
299    /// whether to receive PDUs in strict mode
300    strict: bool,
301    /// whether to accept unknown abstract syntaxes
302    promiscuous: bool,
303    /// Options for the underlying TCP socket
304    socket_options: SocketOptions,
305    /// TLS configuration for the underlying TCP socket
306    #[cfg(feature = "sync-tls")]
307    tls_config: Option<std::sync::Arc<rustls::ServerConfig>>,
308}
309
310impl Default for ServerAssociationOptions<'_, AcceptAny> {
311    fn default() -> Self {
312        ServerAssociationOptions {
313            ae_access_control: AcceptAny,
314            ae_title: "THIS-SCP".into(),
315            application_context_name: "1.2.840.10008.3.1.1.1".into(),
316            abstract_syntax_uids: Vec::new(),
317            transfer_syntax_uids: Vec::new(),
318            protocol_version: 1,
319            max_pdu_length: DEFAULT_MAX_PDU,
320            strict: true,
321            promiscuous: false,
322            socket_options: SocketOptions::default(),
323            #[cfg(feature = "sync-tls")]
324            tls_config: None,
325        }
326    }
327}
328
329impl ServerAssociationOptions<'_, AcceptAny> {
330    /// Create a new set of options for establishing an association.
331    pub fn new() -> Self {
332        Self::default()
333    }
334}
335
336impl<'a, A> ServerAssociationOptions<'a, A>
337where
338    A: AccessControl,
339{
340    /// Change the access control policy to accept any association
341    /// regardless of the specified AE titles.
342    ///
343    /// This is the default behavior when the options are first created.
344    pub fn accept_any(self) -> ServerAssociationOptions<'a, AcceptAny> {
345        self.ae_access_control(AcceptAny)
346    }
347
348    /// Change the access control policy to accept an association
349    /// if the called AE title matches this node's AE title.
350    ///
351    /// The default is to accept any requesting node
352    /// regardless of the specified AE titles.
353    pub fn accept_called_ae_title(self) -> ServerAssociationOptions<'a, AcceptCalledAeTitle> {
354        self.ae_access_control(AcceptCalledAeTitle)
355    }
356
357    /// Change the access control policy.
358    ///
359    /// The default is to accept any requesting node
360    /// regardless of the specified AE titles.
361    pub fn ae_access_control<P>(self, access_control: P) -> ServerAssociationOptions<'a, P>
362    where
363        P: AccessControl,
364    {
365        let ServerAssociationOptions {
366            ae_title,
367            application_context_name,
368            abstract_syntax_uids,
369            transfer_syntax_uids,
370            protocol_version,
371            max_pdu_length,
372            strict,
373            promiscuous,
374            ae_access_control: _,
375            socket_options,
376            #[cfg(feature = "sync-tls")]
377            tls_config,
378        } = self;
379
380        ServerAssociationOptions {
381            ae_access_control: access_control,
382            ae_title,
383            application_context_name,
384            abstract_syntax_uids,
385            transfer_syntax_uids,
386            protocol_version,
387            max_pdu_length,
388            strict,
389            promiscuous,
390            socket_options,
391            #[cfg(feature = "sync-tls")]
392            tls_config,
393        }
394    }
395
396    /// Define the application entity title referring to this DICOM node.
397    ///
398    /// The default is `THIS-SCP`.
399    pub fn ae_title<T>(mut self, ae_title: T) -> Self
400    where
401        T: Into<Cow<'a, str>>,
402    {
403        self.ae_title = ae_title.into();
404        self
405    }
406
407    /// Include this abstract syntax
408    /// in the list of proposed presentation contexts.
409    pub fn with_abstract_syntax<T>(mut self, abstract_syntax_uid: T) -> Self
410    where
411        T: Into<Cow<'a, str>>,
412    {
413        self.abstract_syntax_uids
414            .push(trim_uid(abstract_syntax_uid.into()));
415        self
416    }
417
418    /// Include this transfer syntax in each proposed presentation context.
419    pub fn with_transfer_syntax<T>(mut self, transfer_syntax_uid: T) -> Self
420    where
421        T: Into<Cow<'a, str>>,
422    {
423        self.transfer_syntax_uids
424            .push(trim_uid(transfer_syntax_uid.into()));
425        self
426    }
427
428    /// Override the maximum expected PDU length.
429    pub fn max_pdu_length(mut self, value: u32) -> Self {
430        self.max_pdu_length = value;
431        self
432    }
433
434    /// Override strict mode:
435    /// whether receiving PDUs must not
436    /// surpass the negotiated maximum PDU length.
437    pub fn strict(mut self, strict: bool) -> Self {
438        self.strict = strict;
439        self
440    }
441
442    /// Override promiscuous mode:
443    /// whether to accept unknown abstract syntaxes.
444    pub fn promiscuous(mut self, promiscuous: bool) -> Self {
445        self.promiscuous = promiscuous;
446        self
447    }
448
449    /// Set the read timeout for the underlying TCP socket
450    ///
451    /// This is used to set both the read and write timeout.
452    pub fn read_timeout(self, timeout: Duration) -> Self {
453        Self {
454            socket_options: SocketOptions {
455                read_timeout: Some(timeout),
456                write_timeout: self.socket_options.write_timeout,
457                connection_timeout: self.socket_options.connection_timeout,
458            },
459            ..self
460        }
461    }
462
463    /// Set the write timeout for the underlying TCP socket
464    pub fn write_timeout(self, timeout: Duration) -> Self {
465        Self {
466            socket_options: SocketOptions {
467                read_timeout: self.socket_options.read_timeout,
468                write_timeout: Some(timeout),
469                connection_timeout: self.socket_options.connection_timeout,
470            },
471            ..self
472        }
473    }
474
475    /// Set the TLS configuration for the underlying TCP socket
476    #[cfg(feature = "sync-tls")]
477    pub fn tls_config(mut self, config: impl Into<std::sync::Arc<rustls::ServerConfig>>) -> Self {
478        self.tls_config = Some(config.into());
479        self
480    }
481
482    /// Process an association request PDU
483    ///
484    /// In the success case, returns
485    /// * Pdu to be written back to client
486    /// * Negotiated options
487    /// * Calling AE title
488    ///
489    /// In the error case, returns
490    /// * Pdu to be written back to client
491    /// * Error
492    #[allow(clippy::result_large_err)]
493    fn process_a_association_rq(
494        &self,
495        msg: Pdu,
496    ) -> std::result::Result<(Pdu, NegotiatedOptions), (Pdu, Error)> {
497        match msg {
498            Pdu::AssociationRQ(AssociationRQ {
499                protocol_version,
500                calling_ae_title,
501                called_ae_title,
502                application_context_name,
503                presentation_contexts,
504                user_variables,
505            }) => {
506                if protocol_version != self.protocol_version {
507                    let association_rj = AssociationRJ {
508                        result: AssociationRJResult::Permanent,
509                        source: AssociationRJSource::ServiceUser(
510                            AssociationRJServiceUserReason::NoReasonGiven,
511                        ),
512                    };
513                    let pdu = Pdu::AssociationRJ(association_rj.clone());
514                    return Err((pdu, RejectedSnafu { association_rj }.build()));
515                }
516
517                if application_context_name != self.application_context_name {
518                    let association_rj = AssociationRJ {
519                        result: AssociationRJResult::Permanent,
520                        source: AssociationRJSource::ServiceUser(
521                            AssociationRJServiceUserReason::ApplicationContextNameNotSupported,
522                        ),
523                    };
524                    let pdu = Pdu::AssociationRJ(association_rj.clone());
525                    return Err((pdu, RejectedSnafu { association_rj }.build()));
526                }
527
528                self.ae_access_control
529                    .check_access(
530                        &self.ae_title,
531                        &calling_ae_title,
532                        &called_ae_title,
533                        user_variables
534                            .iter()
535                            .find_map(|user_variable| match user_variable {
536                                UserVariableItem::UserIdentityItem(user_identity) => {
537                                    Some(user_identity)
538                                }
539                                _ => None,
540                            }),
541                    )
542                    .map(Ok)
543                    .unwrap_or_else(|reason| {
544                        let association_rj = AssociationRJ {
545                            result: AssociationRJResult::Permanent,
546                            source: AssociationRJSource::ServiceUser(reason),
547                        };
548                        let pdu = Pdu::AssociationRJ(association_rj.clone());
549                        Err((pdu, RejectedSnafu { association_rj }.build()))
550                    })?;
551
552                // fetch requested maximum PDU length
553                let requestor_max_pdu_length = user_variables
554                    .iter()
555                    .find_map(|item| match item {
556                        UserVariableItem::MaxLength(len) => Some(*len),
557                        _ => None,
558                    })
559                    .unwrap_or(DEFAULT_MAX_PDU);
560
561                // treat 0 as practically unlimited,
562                // so use the largest 32-bit unsigned number
563                let requestor_max_pdu_length = if requestor_max_pdu_length == 0 {
564                    u32::MAX
565                } else {
566                    requestor_max_pdu_length
567                };
568
569                let presentation_contexts_negotiated: Vec<_> = presentation_contexts
570                    .into_iter()
571                    .map(|pc| {
572                        let abstract_syntax = trim_uid(Cow::from(pc.abstract_syntax));
573                        if !self.abstract_syntax_uids.contains(&abstract_syntax)
574                            && !self.promiscuous
575                        {
576                            return PresentationContextNegotiated {
577                                id: pc.id,
578                                reason: PresentationContextResultReason::AbstractSyntaxNotSupported,
579                                transfer_syntax: "1.2.840.10008.1.2".to_string(),
580                                abstract_syntax: abstract_syntax.to_string(),
581                            };
582                        }
583
584                        let (transfer_syntax, reason) = self
585                            .choose_ts(pc.transfer_syntaxes)
586                            .map(|ts| (ts, PresentationContextResultReason::Acceptance))
587                            .unwrap_or_else(|| {
588                                (
589                                    "1.2.840.10008.1.2".to_string(),
590                                    PresentationContextResultReason::TransferSyntaxesNotSupported,
591                                )
592                            });
593
594                        PresentationContextNegotiated {
595                            id: pc.id,
596                            reason,
597                            transfer_syntax,
598                            abstract_syntax: abstract_syntax.to_string(),
599                        }
600                    })
601                    .collect();
602
603                let pdu = Pdu::AssociationAC(AssociationAC {
604                    protocol_version: self.protocol_version,
605                    application_context_name,
606                    presentation_contexts: presentation_contexts_negotiated
607                        .iter()
608                        .map(|pc| PresentationContextResult {
609                            id: pc.id,
610                            reason: pc.reason.clone(),
611                            transfer_syntax: pc.transfer_syntax.clone(),
612                        })
613                        .collect(),
614                    calling_ae_title: calling_ae_title.clone(),
615                    called_ae_title,
616                    user_variables: vec![
617                        UserVariableItem::MaxLength(self.max_pdu_length),
618                        UserVariableItem::ImplementationClassUID(
619                            IMPLEMENTATION_CLASS_UID.to_string(),
620                        ),
621                        UserVariableItem::ImplementationVersionName(
622                            IMPLEMENTATION_VERSION_NAME.to_string(),
623                        ),
624                    ],
625                });
626                Ok((
627                    pdu,
628                    NegotiatedOptions {
629                        peer_max_pdu_length: requestor_max_pdu_length,
630                        user_variables,
631                        presentation_contexts: presentation_contexts_negotiated,
632                        peer_ae_title: calling_ae_title,
633                    },
634                ))
635            }
636            Pdu::ReleaseRQ => Err((Pdu::ReleaseRP, AbortedSnafu.build())),
637            pdu @ Pdu::AssociationAC { .. }
638            | pdu @ Pdu::AssociationRJ { .. }
639            | pdu @ Pdu::PData { .. }
640            | pdu @ Pdu::ReleaseRP
641            | pdu @ Pdu::AbortRQ { .. } => Err((
642                Pdu::AbortRQ {
643                    source: AbortRQSource::ServiceProvider(
644                        AbortRQServiceProviderReason::UnexpectedPdu,
645                    ),
646                },
647                UnexpectedPduSnafu { pdu }.build(),
648            )),
649            pdu @ Pdu::Unknown { .. } => Err((
650                Pdu::AbortRQ {
651                    source: AbortRQSource::ServiceProvider(
652                        AbortRQServiceProviderReason::UnrecognizedPdu,
653                    ),
654                },
655                UnknownPduSnafu { pdu }.build(),
656            )),
657        }
658    }
659
660    /// Negotiate an association with the given TCP stream.
661    pub fn establish(&self, mut socket: TcpStream) -> Result<ServerAssociation<TcpStream>> {
662        ensure!(
663            !self.abstract_syntax_uids.is_empty() || self.promiscuous,
664            MissingAbstractSyntaxSnafu
665        );
666
667        socket
668            .set_read_timeout(self.socket_options.read_timeout)
669            .context(super::SetReadTimeoutSnafu)?;
670        socket
671            .set_write_timeout(self.socket_options.write_timeout)
672            .context(super::SetWriteTimeoutSnafu)?;
673
674        let mut read_buffer = BytesMut::with_capacity(
675            (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
676        );
677        let msg = read_pdu_from_wire(
678            &mut socket,
679            &mut read_buffer,
680            self.max_pdu_length,
681            self.strict,
682        )?;
683        let mut write_buffer: Vec<u8> = Vec::with_capacity(self.max_pdu_length as usize);
684        match self.process_a_association_rq(msg) {
685            Ok((
686                pdu,
687                NegotiatedOptions {
688                    user_variables,
689                    presentation_contexts,
690                    peer_max_pdu_length,
691                    peer_ae_title,
692                },
693            )) => {
694                write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
695                socket.write_all(&write_buffer).context(WireSendSnafu)?;
696                Ok(ServerAssociation {
697                    presentation_contexts,
698                    requestor_max_pdu_length: peer_max_pdu_length,
699                    acceptor_max_pdu_length: self.max_pdu_length,
700                    socket,
701                    client_ae_title: peer_ae_title,
702                    write_buffer,
703                    strict: self.strict,
704                    read_buffer,
705                    user_variables,
706                })
707            }
708            Err((pdu, err)) => {
709                // send the rejection/abort PDU
710                write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
711                socket.write_all(&write_buffer).context(WireSendSnafu)?;
712                Err(err)
713            }
714        }
715    }
716
717    /// Negotiate an association with the given TCP stream using TLS.
718    #[cfg(feature = "sync-tls")]
719    pub fn establish_tls(&self, socket: TcpStream) -> Result<ServerAssociation<TlsStream>> {
720        ensure!(
721            !self.abstract_syntax_uids.is_empty() || self.promiscuous,
722            MissingAbstractSyntaxSnafu
723        );
724        let tls_config = self
725            .tls_config
726            .as_ref()
727            .ok_or_else(|| super::TlsConfigMissingSnafu {}.build())?;
728
729        socket
730            .set_read_timeout(self.socket_options.read_timeout)
731            .context(super::SetReadTimeoutSnafu)?;
732        socket
733            .set_write_timeout(self.socket_options.write_timeout)
734            .context(super::SetWriteTimeoutSnafu)?;
735
736        let conn =
737            rustls::ServerConnection::new(tls_config.clone()).context(super::TlsConnectionSnafu)?;
738        let mut tls_stream = rustls::StreamOwned::new(conn, socket);
739        let mut read_buffer = BytesMut::with_capacity(
740            (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
741        );
742
743        let msg = read_pdu_from_wire(
744            &mut tls_stream,
745            &mut read_buffer,
746            self.max_pdu_length,
747            self.strict,
748        )?;
749        let mut write_buffer: Vec<u8> = Vec::with_capacity(self.max_pdu_length as usize);
750        match self.process_a_association_rq(msg) {
751            Ok((
752                pdu,
753                NegotiatedOptions {
754                    user_variables,
755                    presentation_contexts,
756                    peer_max_pdu_length,
757                    peer_ae_title,
758                },
759            )) => {
760                write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
761                tls_stream.write_all(&write_buffer).context(WireSendSnafu)?;
762                Ok(ServerAssociation {
763                    presentation_contexts,
764                    requestor_max_pdu_length: peer_max_pdu_length,
765                    acceptor_max_pdu_length: self.max_pdu_length,
766                    socket: tls_stream,
767                    client_ae_title: peer_ae_title,
768                    write_buffer,
769                    strict: self.strict,
770                    read_buffer,
771                    user_variables,
772                })
773            }
774            Err((pdu, err)) => {
775                // send the rejection/abort PDU
776                write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
777                tls_stream.write_all(&write_buffer).context(WireSendSnafu)?;
778                Err(err)
779            }
780        }
781    }
782
783    /// From a sequence of transfer syntaxes,
784    /// choose the first transfer syntax to
785    /// - be on the options' list of transfer syntaxes, and
786    /// - be supported by the main transfer syntax registry.
787    ///
788    /// If the options' list is empty,
789    /// accept the first transfer syntax supported.
790    fn choose_ts<I, T>(&self, it: I) -> Option<T>
791    where
792        I: IntoIterator<Item = T>,
793        T: AsRef<str>,
794    {
795        if self.transfer_syntax_uids.is_empty() {
796            return choose_supported(it);
797        }
798
799        it.into_iter().find(|ts| {
800            let ts = ts.as_ref();
801            if self.transfer_syntax_uids.is_empty() {
802                ts.trim_end_matches(|c: char| c.is_whitespace() || c == '\0') == "1.2.840.10008.1.2"
803            } else {
804                self.transfer_syntax_uids.contains(&trim_uid(ts.into())) && is_supported(ts)
805            }
806        })
807    }
808}
809
810/// A DICOM upper level association from the perspective
811/// of an accepting application entity.
812///
813/// The most common operations of an established association are
814/// [`send`](SyncAssociation::send)
815/// and [`receive`](SyncAssociation::receive).
816/// Sending large P-Data fragments may be easier through the P-Data sender
817/// abstraction (see [`send_pdata`](SyncAssociation::send_pdata)).
818///
819/// When the value falls out of scope,
820/// the program will shut down the underlying TCP connection.
821#[derive(Debug)]
822pub struct ServerAssociation<S> {
823    /// The accorded presentation contexts
824    presentation_contexts: Vec<PresentationContextNegotiated>,
825    /// The maximum PDU length that the remote application entity accepts
826    requestor_max_pdu_length: u32,
827    /// The maximum PDU length that this application entity is expecting to receive
828    acceptor_max_pdu_length: u32,
829    /// The TCP stream to the other DICOM node
830    socket: S,
831    /// The application entity title of the other DICOM node
832    client_ae_title: String,
833    /// Reusable buffer used for sending PDUs on the wire
834    /// prevents reallocation on each send
835    write_buffer: Vec<u8>,
836    /// whether to receive PDUs in strict mode
837    strict: bool,
838    /// Read buffer from the socket
839    read_buffer: bytes::BytesMut,
840    /// User variables received from the peer
841    user_variables: Vec<UserVariableItem>,
842}
843
844// compatibility filler, remove in 0.10.0
845impl<S> ServerAssociation<S> {
846    /// Obtain a view of the negotiated presentation contexts.
847    pub fn presentation_contexts(&self) -> &[PresentationContextNegotiated] {
848        &self.presentation_contexts
849    }
850
851    /// Retrieve the maximum PDU length
852    /// that the association acceptor is expecting to receive.
853    pub fn acceptor_max_pdu_length(&self) -> u32 {
854        self.acceptor_max_pdu_length
855    }
856
857    /// Retrieve the maximum PDU length
858    /// that the association requestor is expecting to receive.
859    pub fn requestor_max_pdu_length(&self) -> u32 {
860        self.requestor_max_pdu_length
861    }
862
863    /// Obtain the remote DICOM node's application entity title.
864    #[deprecated(
865        since = "0.9.1",
866        note = "Call `peer_ae_title` from trait `Association`"
867    )]
868    pub fn client_ae_title(&self) -> &str {
869        &self.client_ae_title
870    }
871}
872
873impl<S> ServerAssociation<S>
874where
875    S: std::io::Read + std::io::Write + CloseSocket,
876{
877    /// Send a PDU message to the other intervenient.
878    pub fn send(&mut self, msg: &Pdu) -> Result<()> {
879        SyncAssociation::send(self, msg)
880    }
881
882    /// Read a PDU message from the other intervenient.
883    pub fn receive(&mut self) -> Result<Pdu> {
884        SyncAssociation::receive(self)
885    }
886
887    /// Send a provider initiated abort message
888    /// and shut down the TCP connection,
889    /// terminating the association.
890    pub fn abort(self) -> Result<()> {
891        SyncAssociation::abort(self)
892    }
893
894    /// Prepare a P-Data writer for sending
895    /// one or more data item PDUs.
896    ///
897    /// Returns a writer which automatically
898    /// splits the inner data into separate PDUs if necessary.
899    pub fn send_pdata(
900        &mut self,
901        presentation_context_id: u8,
902    ) -> crate::association::pdata::PDataWriter<&mut S> {
903        SyncAssociation::send_pdata(self, presentation_context_id)
904    }
905
906    /// Prepare a P-Data reader for receiving
907    /// one or more data item PDUs.
908    ///
909    /// Returns a reader which automatically
910    /// receives more data PDUs once the bytes collected are consumed.
911    pub fn receive_pdata(&mut self) -> crate::association::pdata::PDataReader<'_, &mut S> {
912        SyncAssociation::receive_pdata(self)
913    }
914
915    /// Obtain access to the inner stream
916    /// connected to the association acceptor.
917    ///
918    /// This can be used to send the PDU in semantic fragments of the message,
919    /// thus using less memory.
920    ///
921    /// **Note:** reading and writing should be done with care
922    /// to avoid inconsistencies in the association state.
923    /// Do not call `send` and `receive` while not in a PDU boundary.
924    pub fn inner_stream(&mut self) -> &mut S {
925        SyncAssociation::inner_stream(self)
926    }
927}
928
929impl<S> Association for ServerAssociation<S>
930where
931    S: std::io::Read + std::io::Write + CloseSocket,
932{
933    /// Obtain a view of the negotiated presentation contexts.
934    fn presentation_contexts(&self) -> &[PresentationContextNegotiated] {
935        &self.presentation_contexts
936    }
937
938    /// Retrieve the maximum PDU length
939    /// that the association acceptor is expecting to receive.
940    fn acceptor_max_pdu_length(&self) -> u32 {
941        self.acceptor_max_pdu_length
942    }
943
944    /// Retrieve the maximum PDU length
945    /// that the association requestor is expecting to receive.
946    fn requestor_max_pdu_length(&self) -> u32 {
947        self.requestor_max_pdu_length
948    }
949
950    /// Retrieve the maximum PDU length that this application entity
951    /// (the association acceptor) is expecting to receive.
952    fn local_max_pdu_length(&self) -> u32 {
953        self.acceptor_max_pdu_length
954    }
955
956    /// Retrieve the maximum PDU length that the peer application entity
957    /// (the association requestor) is expecting to receive.
958    fn peer_max_pdu_length(&self) -> u32 {
959        self.requestor_max_pdu_length
960    }
961
962    /// Obtain the remote DICOM node's application entity title.
963    fn peer_ae_title(&self) -> &str {
964        &self.client_ae_title
965    }
966
967    /// Retrieve the user variables that were taken from the server.
968    ///
969    /// It usually contains the maximum PDU length,
970    /// the implementation class UID, and the implementation version name.
971    fn user_variables(&self) -> &[UserVariableItem] {
972        &self.user_variables
973    }
974}
975
976impl<S> SyncAssociationSealed<S> for ServerAssociation<S>
977where
978    S: std::io::Read + std::io::Write + CloseSocket,
979{
980    fn send(&mut self, pdu: &Pdu) -> Result<()> {
981        self.write_buffer.clear();
982        encode_pdu(
983            &mut self.write_buffer,
984            pdu,
985            self.requestor_max_pdu_length + PDU_HEADER_SIZE,
986        )?;
987        self.socket
988            .write_all(&self.write_buffer)
989            .context(WireSendSnafu)
990    }
991
992    fn receive(&mut self) -> Result<Pdu> {
993        read_pdu_from_wire(
994            &mut self.socket,
995            &mut self.read_buffer,
996            self.acceptor_max_pdu_length,
997            self.strict,
998        )
999    }
1000
1001    fn close(&mut self) -> std::io::Result<()> {
1002        self.socket.close()
1003    }
1004}
1005
1006impl<S> SyncAssociation<S> for ServerAssociation<S>
1007where
1008    S: std::io::Read + std::io::Write + CloseSocket,
1009{
1010    fn inner_stream(&mut self) -> &mut S {
1011        &mut self.socket
1012    }
1013
1014    fn get_mut(&mut self) -> (&mut S, &mut BytesMut) {
1015        let Self {
1016            socket,
1017            read_buffer,
1018            ..
1019        } = self;
1020        (socket, read_buffer)
1021    }
1022}
1023
1024/// Check that a transfer syntax repository
1025/// supports the given transfer syntax,
1026/// meaning that it can parse and decode DICOM data sets.
1027///
1028/// ```
1029/// # use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
1030/// # use dicom_ul::association::server::is_supported_with_repo;
1031/// // Implicit VR Little Endian is guaranteed to be supported
1032/// assert!(is_supported_with_repo(TransferSyntaxRegistry, "1.2.840.10008.1.2"));
1033/// ```
1034pub fn is_supported_with_repo<R>(ts_repo: R, ts_uid: &str) -> bool
1035where
1036    R: TransferSyntaxIndex,
1037{
1038    ts_repo
1039        .get(ts_uid)
1040        .filter(|ts| !ts.is_unsupported())
1041        .is_some()
1042}
1043
1044/// Check that the main transfer syntax registry
1045/// supports the given transfer syntax,
1046/// meaning that it can parse and decode DICOM data sets.
1047///
1048/// ```
1049/// # use dicom_ul::association::server::is_supported;
1050/// // Implicit VR Little Endian is guaranteed to be supported
1051/// assert!(is_supported("1.2.840.10008.1.2"));
1052/// ```
1053pub fn is_supported(ts_uid: &str) -> bool {
1054    is_supported_with_repo(TransferSyntaxRegistry, ts_uid)
1055}
1056
1057/// From a sequence of transfer syntaxes,
1058/// choose the first transfer syntax to be supported
1059/// by the given transfer syntax repository.
1060pub fn choose_supported_with_repo<R, I, T>(ts_repo: R, it: I) -> Option<T>
1061where
1062    R: TransferSyntaxIndex,
1063    I: IntoIterator<Item = T>,
1064    T: AsRef<str>,
1065{
1066    it.into_iter()
1067        .find(|ts| is_supported_with_repo(&ts_repo, ts.as_ref()))
1068}
1069
1070/// From a sequence of transfer syntaxes,
1071/// choose the first transfer syntax to be supported
1072/// by the main transfer syntax registry.
1073pub fn choose_supported<I, T>(it: I) -> Option<T>
1074where
1075    I: IntoIterator<Item = T>,
1076    T: AsRef<str>,
1077{
1078    it.into_iter().find(|ts| is_supported(ts.as_ref()))
1079}
1080
1081#[cfg(feature = "async")]
1082impl<A> ServerAssociationOptions<'_, A>
1083where
1084    A: AccessControl,
1085{
1086    /// Negotiate an association with the given TCP stream.
1087    pub async fn establish_async(
1088        &self,
1089        mut socket: tokio::net::TcpStream,
1090    ) -> Result<AsyncServerAssociation<tokio::net::TcpStream>> {
1091        use tokio::io::AsyncWriteExt;
1092        ensure!(
1093            !self.abstract_syntax_uids.is_empty() || self.promiscuous,
1094            MissingAbstractSyntaxSnafu
1095        );
1096        let read_timeout = self.socket_options.read_timeout;
1097        let task = async {
1098            let mut read_buffer = BytesMut::with_capacity(
1099                (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1100            );
1101            let pdu = super::read_pdu_from_wire_async(
1102                &mut socket,
1103                &mut read_buffer,
1104                self.max_pdu_length,
1105                self.strict,
1106            )
1107            .await?;
1108
1109            let mut write_buffer: Vec<u8> =
1110                Vec::with_capacity((DEFAULT_MAX_PDU + PDU_HEADER_SIZE) as usize);
1111            match self.process_a_association_rq(pdu) {
1112                Ok((
1113                    pdu,
1114                    NegotiatedOptions {
1115                        user_variables,
1116                        presentation_contexts,
1117                        peer_max_pdu_length,
1118                        peer_ae_title,
1119                    },
1120                )) => {
1121                    write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
1122                    socket
1123                        .write_all(&write_buffer)
1124                        .await
1125                        .context(WireSendSnafu)?;
1126                    Ok(AsyncServerAssociation {
1127                        presentation_contexts,
1128                        requestor_max_pdu_length: peer_max_pdu_length,
1129                        acceptor_max_pdu_length: self.max_pdu_length,
1130                        socket,
1131                        client_ae_title: peer_ae_title,
1132                        write_buffer,
1133                        strict: self.strict,
1134                        read_buffer,
1135                        read_timeout: self.socket_options.read_timeout,
1136                        write_timeout: self.socket_options.write_timeout,
1137                        user_variables,
1138                    })
1139                }
1140                Err((pdu, err)) => {
1141                    // send the rejection/abort PDU
1142                    write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
1143                    socket
1144                        .write_all(&write_buffer)
1145                        .await
1146                        .context(WireSendSnafu)?;
1147                    Err(err)
1148                }
1149            }
1150        };
1151        super::timeout(read_timeout, task).await
1152    }
1153
1154    /// Negotiate an association with the given TCP stream.
1155    #[cfg(feature = "async-tls")]
1156    pub async fn establish_tls_async(
1157        &self,
1158        socket: tokio::net::TcpStream,
1159    ) -> Result<AsyncServerAssociation<AsyncTlsStream>> {
1160        use tokio::io::AsyncWriteExt;
1161        use tokio_rustls::TlsAcceptor;
1162
1163        ensure!(
1164            !self.abstract_syntax_uids.is_empty() || self.promiscuous,
1165            MissingAbstractSyntaxSnafu
1166        );
1167        let tls_config = self
1168            .tls_config
1169            .as_ref()
1170            .ok_or_else(|| crate::association::TlsConfigMissingSnafu {}.build())?;
1171        let acceptor = TlsAcceptor::from(tls_config.clone());
1172        let mut socket = acceptor
1173            .accept(socket)
1174            .await
1175            .context(crate::association::ConnectSnafu)?;
1176        let read_timeout = self.socket_options.read_timeout;
1177        let task = async {
1178            let mut read_buffer = BytesMut::with_capacity(
1179                (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1180            );
1181            let pdu = super::read_pdu_from_wire_async(
1182                &mut socket,
1183                &mut read_buffer,
1184                self.max_pdu_length,
1185                self.strict,
1186            )
1187            .await?;
1188
1189            let mut write_buffer: Vec<u8> =
1190                Vec::with_capacity((DEFAULT_MAX_PDU + PDU_HEADER_SIZE) as usize);
1191            match self.process_a_association_rq(pdu) {
1192                Ok((
1193                    pdu,
1194                    NegotiatedOptions {
1195                        user_variables,
1196                        presentation_contexts,
1197                        peer_max_pdu_length,
1198                        peer_ae_title,
1199                    },
1200                )) => {
1201                    write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
1202                    socket
1203                        .write_all(&write_buffer)
1204                        .await
1205                        .context(WireSendSnafu)?;
1206                    Ok(AsyncServerAssociation {
1207                        presentation_contexts,
1208                        requestor_max_pdu_length: peer_max_pdu_length,
1209                        acceptor_max_pdu_length: self.max_pdu_length,
1210                        socket,
1211                        client_ae_title: peer_ae_title,
1212                        write_buffer,
1213                        strict: self.strict,
1214                        read_buffer,
1215                        read_timeout: self.socket_options.read_timeout,
1216                        write_timeout: self.socket_options.write_timeout,
1217                        user_variables,
1218                    })
1219                }
1220                Err((pdu, err)) => {
1221                    // send the rejection/abort PDU
1222                    write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
1223                    socket
1224                        .write_all(&write_buffer)
1225                        .await
1226                        .context(WireSendSnafu)?;
1227                    Err(err)
1228                }
1229            }
1230        };
1231        super::timeout(read_timeout, task).await
1232    }
1233}
1234
1235/// An async DICOM upper level association from the perspective
1236/// of an accepting application entity.
1237///
1238/// The most common operations of an established association are
1239/// [`send`](crate::association::AsyncAssociation::send)
1240/// and [`receive`](crate::association::AsyncAssociation::receive).
1241/// Sending large P-Data fragments may be easier through the P-Data sender
1242/// abstraction (see [`send_pdata`](crate::association::AsyncAssociation::send_pdata)).
1243///
1244/// When the value falls out of scope,
1245/// the program will shut down the underlying TCP connection.
1246#[cfg(feature = "async")]
1247#[derive(Debug)]
1248pub struct AsyncServerAssociation<S> {
1249    /// The accorded presentation contexts
1250    presentation_contexts: Vec<PresentationContextNegotiated>,
1251    /// The maximum PDU length that the remote application entity accepts
1252    requestor_max_pdu_length: u32,
1253    /// The maximum PDU length that this application entity is expecting to receive
1254    acceptor_max_pdu_length: u32,
1255    /// The TCP stream to the other DICOM node
1256    socket: S,
1257    /// The application entity title of the other DICOM node
1258    client_ae_title: String,
1259    /// write buffer to send fully assembled PDUs on wire
1260    write_buffer: Vec<u8>,
1261    /// whether to receive PDUs in strict mode
1262    strict: bool,
1263    /// Read buffer from the socket
1264    read_buffer: bytes::BytesMut,
1265    /// Timeout for individual receive operations
1266    read_timeout: Option<std::time::Duration>,
1267    /// Timeout for individual send operations
1268    write_timeout: Option<std::time::Duration>,
1269    /// User variables received from the peer
1270    user_variables: Vec<UserVariableItem>,
1271}
1272
1273#[cfg(feature = "async")]
1274impl<S> Association for AsyncServerAssociation<S>
1275where
1276    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send,
1277{
1278    /// Retrieve the maximum PDU length
1279    /// that the association acceptor is expecting to receive.
1280    fn acceptor_max_pdu_length(&self) -> u32 {
1281        self.acceptor_max_pdu_length
1282    }
1283
1284    /// Retrieve the maximum PDU length
1285    /// that the association requestor is expecting to receive.
1286    fn requestor_max_pdu_length(&self) -> u32 {
1287        self.requestor_max_pdu_length
1288    }
1289
1290    /// Retrieve the maximum PDU length that this application entity
1291    /// (the association acceptor) is expecting to receive.
1292    fn local_max_pdu_length(&self) -> u32 {
1293        self.acceptor_max_pdu_length
1294    }
1295
1296    /// Retrieve the maximum PDU length that the peer application entity
1297    /// (the association requestor) is expecting to receive.
1298    fn peer_max_pdu_length(&self) -> u32 {
1299        self.requestor_max_pdu_length
1300    }
1301
1302    /// Obtain a view of the negotiated presentation contexts.
1303    fn presentation_contexts(&self) -> &[PresentationContextNegotiated] {
1304        &self.presentation_contexts
1305    }
1306
1307    /// Obtain the remote DICOM node's application entity title.
1308    fn peer_ae_title(&self) -> &str {
1309        &self.client_ae_title
1310    }
1311
1312    fn user_variables(&self) -> &[UserVariableItem] {
1313        &self.user_variables
1314    }
1315}
1316
1317#[cfg(feature = "async")]
1318impl<S> crate::association::private::AsyncAssociationSealed<S> for AsyncServerAssociation<S>
1319where
1320    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send,
1321{
1322    /// Send a PDU message to the other intervenient.
1323    async fn send(&mut self, msg: &Pdu) -> Result<()> {
1324        use tokio::io::AsyncWriteExt;
1325        self.write_buffer.clear();
1326        super::timeout(self.write_timeout, async {
1327            encode_pdu(
1328                &mut self.write_buffer,
1329                msg,
1330                self.requestor_max_pdu_length + PDU_HEADER_SIZE,
1331            )?;
1332            self.socket
1333                .write_all(&self.write_buffer)
1334                .await
1335                .context(WireSendSnafu)
1336        })
1337        .await
1338    }
1339
1340    /// Read a PDU message from the other intervenient.
1341    async fn receive(&mut self) -> Result<Pdu> {
1342        super::timeout(self.read_timeout, async {
1343            super::read_pdu_from_wire_async(
1344                &mut self.socket,
1345                &mut self.read_buffer,
1346                self.acceptor_max_pdu_length,
1347                self.strict,
1348            )
1349            .await
1350        })
1351        .await
1352    }
1353
1354    async fn close(&mut self) -> std::io::Result<()> {
1355        use tokio::io::AsyncWriteExt;
1356        self.socket.shutdown().await
1357    }
1358}
1359
1360#[cfg(feature = "async")]
1361impl<S> crate::association::AsyncAssociation<S> for AsyncServerAssociation<S>
1362where
1363    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send,
1364{
1365    fn inner_stream(&mut self) -> &mut S {
1366        &mut self.socket
1367    }
1368
1369    fn get_mut(&mut self) -> (&mut S, &mut bytes::BytesMut) {
1370        let Self {
1371            socket,
1372            read_buffer,
1373            ..
1374        } = self;
1375        (socket, read_buffer)
1376    }
1377}
1378
1379// compatibility filler, remove in 0.10.0
1380#[cfg(feature = "async")]
1381impl<S> AsyncServerAssociation<S> {
1382    /// Obtain a view of the negotiated presentation contexts.
1383    pub fn presentation_contexts(&self) -> &[PresentationContextNegotiated] {
1384        &self.presentation_contexts
1385    }
1386
1387    /// Retrieve the maximum PDU length
1388    /// that the association acceptor is expecting to receive.
1389    pub fn acceptor_max_pdu_length(&self) -> u32 {
1390        self.acceptor_max_pdu_length
1391    }
1392
1393    /// Retrieve the maximum PDU length
1394    /// that the association requestor is expecting to receive.
1395    pub fn requestor_max_pdu_length(&self) -> u32 {
1396        self.requestor_max_pdu_length
1397    }
1398
1399    /// Obtain the remote DICOM node's application entity title.
1400    #[deprecated(
1401        since = "0.9.1",
1402        note = "Call `peer_ae_title` from trait `Association`"
1403    )]
1404    pub fn client_ae_title(&self) -> &str {
1405        &self.client_ae_title
1406    }
1407}
1408
1409// compatibility filler, remove in 0.10.0
1410#[cfg(feature = "async")]
1411impl<S> AsyncServerAssociation<S>
1412where
1413    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send,
1414{
1415    /// Send a PDU message to the other intervenient.
1416    pub async fn send(&mut self, msg: &Pdu) -> Result<()> {
1417        AsyncAssociation::send(self, msg).await
1418    }
1419
1420    /// Read a PDU message from the other intervenient.
1421    pub async fn receive(&mut self) -> Result<Pdu> {
1422        AsyncAssociation::receive(self).await
1423    }
1424
1425    /// Iniate a graceful release of the association.
1426    ///
1427    /// A DIMSE A-RELEASE transaction is initiated by this application entity,
1428    /// and the underlying socket is closed once settled.
1429    ///
1430    /// Note that implementers of this trait
1431    /// do not try to release the association on [`Drop`],
1432    /// so remember to call `release` explicitly
1433    /// at the end of all DIMSE transactions.
1434    pub async fn release(self) -> Result<()> {
1435        AsyncAssociation::release(self).await
1436    }
1437
1438    /// Send a provider initiated abort message
1439    /// and shut down the TCP connection,
1440    /// terminating the association.
1441    pub async fn abort(self) -> Result<()> {
1442        AsyncAssociation::abort(self).await
1443    }
1444
1445    /// Prepare a P-Data writer for sending
1446    /// one or more data item PDUs.
1447    ///
1448    /// Returns a writer which automatically
1449    /// splits the inner data into separate PDUs if necessary.
1450    pub fn send_pdata(
1451        &mut self,
1452        presentation_context_id: u8,
1453    ) -> crate::association::pdata::non_blocking::AsyncPDataWriter<&mut S> {
1454        AsyncAssociation::send_pdata(self, presentation_context_id)
1455    }
1456
1457    /// Prepare a P-Data reader for receiving
1458    /// one or more data item PDUs.
1459    ///
1460    /// Returns a reader which automatically
1461    /// receives more data PDUs once the bytes collected are consumed.
1462    pub fn receive_pdata(&mut self) -> crate::association::pdata::PDataReader<'_, &mut S> {
1463        AsyncAssociation::receive_pdata(self)
1464    }
1465
1466    /// Obtain access to the inner stream
1467    /// connected to the association acceptor.
1468    ///
1469    /// This can be used to send the PDU in semantic fragments of the message,
1470    /// thus using less memory.
1471    ///
1472    /// **Note:** reading and writing should be done with care
1473    /// to avoid inconsistencies in the association state.
1474    /// Do not call `send` and `receive` while not in a PDU boundary.
1475    pub fn inner_stream(&mut self) -> &mut S {
1476        AsyncAssociation::inner_stream(self)
1477    }
1478}
1479
1480#[cfg(test)]
1481mod tests {
1482    use super::*;
1483
1484    #[test]
1485    fn test_choose_supported() {
1486        assert_eq!(choose_supported(vec!["1.1.1.1.1"]), None,);
1487
1488        // string slices, impl VR first
1489        assert_eq!(
1490            choose_supported(vec!["1.2.840.10008.1.2", "1.2.840.10008.1.2.1"]),
1491            Some("1.2.840.10008.1.2"),
1492        );
1493
1494        // heap allocated strings slices, expl VR first
1495        assert_eq!(
1496            choose_supported(vec![
1497                "1.2.840.10008.1.2.1".to_string(),
1498                "1.2.840.10008.1.2".to_string()
1499            ]),
1500            Some("1.2.840.10008.1.2.1".to_string()),
1501        );
1502    }
1503
1504    impl<'a, A> ServerAssociationOptions<'a, A>
1505    where
1506        A: AccessControl,
1507    {
1508        // Broken implementation of server establish which sends an extra pdu during establish
1509        pub(crate) fn establish_with_extra_pdus(
1510            &self,
1511            mut socket: std::net::TcpStream,
1512            extra_pdus: Vec<Pdu>,
1513        ) -> Result<ServerAssociation<TcpStream>> {
1514            let mut read_buffer = BytesMut::with_capacity(
1515                (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1516            );
1517            let pdu = read_pdu_from_wire(
1518                &mut socket,
1519                &mut read_buffer,
1520                self.max_pdu_length,
1521                self.strict,
1522            )?;
1523            let (
1524                pdu,
1525                NegotiatedOptions {
1526                    user_variables,
1527                    presentation_contexts,
1528                    peer_max_pdu_length,
1529                    peer_ae_title,
1530                },
1531            ) = self
1532                .process_a_association_rq(pdu)
1533                .expect("Could not parse association req");
1534
1535            let mut write_buffer: Vec<u8> =
1536                Vec::with_capacity((DEFAULT_MAX_PDU + PDU_HEADER_SIZE) as usize);
1537            write_pdu(&mut write_buffer, &pdu).context(SendPduSnafu)?;
1538            for extra_pdu in extra_pdus {
1539                write_pdu(&mut write_buffer, &extra_pdu).context(SendPduSnafu)?;
1540            }
1541            socket.write_all(&write_buffer).context(WireSendSnafu)?;
1542
1543            Ok(ServerAssociation {
1544                presentation_contexts,
1545                requestor_max_pdu_length: peer_max_pdu_length,
1546                acceptor_max_pdu_length: self.max_pdu_length,
1547                socket,
1548                client_ae_title: peer_ae_title,
1549                write_buffer,
1550                read_buffer,
1551                strict: self.strict,
1552                user_variables,
1553            })
1554        }
1555
1556        // Broken implementation of server establish which sends an extra pdu during establish
1557        #[cfg(feature = "async")]
1558        pub(crate) async fn establish_with_extra_pdus_async(
1559            &self,
1560            mut socket: tokio::net::TcpStream,
1561            extra_pdus: Vec<Pdu>,
1562        ) -> Result<AsyncServerAssociation<tokio::net::TcpStream>> {
1563            use tokio::io::AsyncWriteExt;
1564
1565            use crate::association::read_pdu_from_wire_async;
1566
1567            let mut read_buffer = BytesMut::with_capacity(
1568                (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1569            );
1570            let pdu = read_pdu_from_wire_async(
1571                &mut socket,
1572                &mut read_buffer,
1573                self.max_pdu_length,
1574                self.strict,
1575            )
1576            .await?;
1577            let (
1578                pdu,
1579                NegotiatedOptions {
1580                    user_variables,
1581                    presentation_contexts,
1582                    peer_max_pdu_length,
1583                    peer_ae_title,
1584                },
1585            ) = self
1586                .process_a_association_rq(pdu)
1587                .expect("Could not parse association req");
1588
1589            let mut buffer: Vec<u8> = Vec::with_capacity(
1590                (peer_max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1591            );
1592            write_pdu(&mut buffer, &pdu).context(SendPduSnafu)?;
1593            for extra_pdu in extra_pdus {
1594                write_pdu(&mut buffer, &extra_pdu).context(SendPduSnafu)?;
1595            }
1596            socket.write_all(&buffer).await.context(WireSendSnafu)?;
1597
1598            Ok(AsyncServerAssociation {
1599                presentation_contexts,
1600                requestor_max_pdu_length: peer_max_pdu_length,
1601                acceptor_max_pdu_length: self.max_pdu_length,
1602                socket,
1603                client_ae_title: peer_ae_title,
1604                write_buffer: buffer,
1605                strict: self.strict,
1606                read_buffer: BytesMut::with_capacity(
1607                    (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1608                ),
1609                user_variables,
1610                read_timeout: self.socket_options.read_timeout,
1611                write_timeout: self.socket_options.write_timeout,
1612            })
1613        }
1614
1615        // Broken implementation of server establish which reproduces behavior that #589 introduced
1616        pub fn broken_establish(
1617            &self,
1618            mut socket: TcpStream,
1619        ) -> Result<ServerAssociation<TcpStream>> {
1620            let mut read_buffer = BytesMut::with_capacity(
1621                (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1622            );
1623            let msg = read_pdu_from_wire(
1624                &mut socket,
1625                &mut read_buffer,
1626                self.max_pdu_length,
1627                self.strict,
1628            )?;
1629            let (
1630                pdu,
1631                NegotiatedOptions {
1632                    user_variables,
1633                    presentation_contexts,
1634                    peer_max_pdu_length,
1635                    peer_ae_title,
1636                },
1637            ) = self
1638                .process_a_association_rq(msg)
1639                .expect("Could not parse association req");
1640            let mut buffer: Vec<u8> = Vec::with_capacity(
1641                (peer_max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1642            );
1643            write_pdu(&mut buffer, &pdu).context(SendPduSnafu)?;
1644            socket.write_all(&buffer).context(WireSendSnafu)?;
1645            Ok(ServerAssociation {
1646                presentation_contexts,
1647                requestor_max_pdu_length: peer_max_pdu_length,
1648                acceptor_max_pdu_length: self.max_pdu_length,
1649                socket,
1650                client_ae_title: peer_ae_title,
1651                write_buffer: buffer,
1652                strict: self.strict,
1653                read_buffer: BytesMut::with_capacity(
1654                    (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1655                ),
1656                user_variables,
1657            })
1658        }
1659
1660        // Broken implementation of server establish which reproduces behavior that #589 introduced
1661        #[cfg(feature = "async")]
1662        pub async fn broken_establish_async(
1663            &self,
1664            mut socket: tokio::net::TcpStream,
1665        ) -> Result<AsyncServerAssociation<tokio::net::TcpStream>> {
1666            use tokio::io::AsyncWriteExt;
1667
1668            use crate::association::read_pdu_from_wire_async;
1669
1670            let mut read_buffer = BytesMut::with_capacity(
1671                (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1672            );
1673            let msg = read_pdu_from_wire_async(
1674                &mut socket,
1675                &mut read_buffer,
1676                self.max_pdu_length,
1677                self.strict,
1678            )
1679            .await?;
1680            let (
1681                pdu,
1682                NegotiatedOptions {
1683                    user_variables,
1684                    presentation_contexts,
1685                    peer_max_pdu_length,
1686                    peer_ae_title,
1687                },
1688            ) = self
1689                .process_a_association_rq(msg)
1690                .expect("Could not parse association req");
1691            let mut buffer: Vec<u8> = Vec::with_capacity(
1692                (peer_max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1693            );
1694            write_pdu(&mut buffer, &pdu).context(SendPduSnafu)?;
1695            socket.write_all(&buffer).await.context(WireSendSnafu)?;
1696            Ok(AsyncServerAssociation {
1697                presentation_contexts,
1698                requestor_max_pdu_length: peer_max_pdu_length,
1699                acceptor_max_pdu_length: self.max_pdu_length,
1700                socket,
1701                client_ae_title: peer_ae_title,
1702                write_buffer: buffer,
1703                strict: self.strict,
1704                read_buffer: BytesMut::with_capacity(
1705                    (self.max_pdu_length.min(LARGE_PDU_SIZE) + PDU_HEADER_SIZE) as usize,
1706                ),
1707                read_timeout: self.socket_options.read_timeout,
1708                write_timeout: self.socket_options.write_timeout,
1709                user_variables,
1710            })
1711        }
1712    }
1713}