From c3d4c8e0930a2f4fc0e6f5385b1031ae3c340487 Mon Sep 17 00:00:00 2001 From: Marco Eichelberg Date: Fri, 1 May 2020 19:18:18 +0200 Subject: [PATCH] Added alternative service provision API to DcmSCP. Added alternative service provision interface to class DcmSCP that can be used instead of DcmSCP::listen(). The alternative interface uses two separate calls, where the first one, openListenPort(), opens the TCP port and executes listen(3), and the second one, acceptAssociations(), runs the service provision code just as listen() does. The alternative interface allows incoming network connections to be placed on the listen backlog while the caller performs other work (such as a Storage Commitment SCU sending out an N-ACTION-RQ) and to be accepted and handled once acceptAssociations() is run. --- dcmnet/include/dcmtk/dcmnet/scp.h | 24 +++++++++++- dcmnet/libsrc/scp.cc | 61 ++++++++++++++++++++++++++----- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/dcmnet/include/dcmtk/dcmnet/scp.h b/dcmnet/include/dcmtk/dcmnet/scp.h index 9e80ba86fd..17c54c1dfb 100644 --- a/dcmnet/include/dcmtk/dcmnet/scp.h +++ b/dcmnet/include/dcmtk/dcmnet/scp.h @@ -1,6 +1,6 @@ /* * - * Copyright (C) 2009-2018, OFFIS e.V. + * Copyright (C) 2009-2020, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by @@ -174,6 +174,25 @@ class DCMTK_DCMNET_EXPORT DcmSCP */ virtual OFCondition listen(); + /** Alternative interface to providing the implemented services to SCUs. + * This method opens the TCP port for incoming connections, drops root privileges + * if necessary and then returns. The caller can then perform short other operations + * and finally call acceptAssociations(). If any SCU tries to connect between + * this method and the call to acceptAssociations(), they are placed on the + * TCP listen backlog and will be handled by acceptAssociations() unless they + * time out or the backlog overflows (the size of the backlog is defined by the + * PRV_LISTENBACKLOG macro). + * @return EC_Normal if successful, an error code otherwise. + */ + virtual OFCondition openListenPort(); + + /** Alternative interface to providing the implemented services to SCUs. + * This method should be called after a call to openListenPort(). + * @return The result. Per default, the method only returns in case of fatal errors. + * See documentation of listen() method on how to stop listening in a controlled way. + */ + virtual OFCondition acceptAssociations(); + /* ************************************************************* */ /* Set methods for configuring SCP behavior */ /* ************************************************************* */ @@ -1117,6 +1136,9 @@ class DCMTK_DCMNET_EXPORT DcmSCP static void callbackRECEIVEProgress(void* callbackContext, unsigned long byteCount); private: + /// Network instance run by this SCP + T_ASC_Network* m_network; + /// Current association run by this SCP T_ASC_Association* m_assoc; diff --git a/dcmnet/libsrc/scp.cc b/dcmnet/libsrc/scp.cc index 3f051cd453..e5f57a532e 100644 --- a/dcmnet/libsrc/scp.cc +++ b/dcmnet/libsrc/scp.cc @@ -29,8 +29,9 @@ // ---------------------------------------------------------------------------- DcmSCP::DcmSCP() - : m_assoc(NULL) - , m_cfg() +: m_network(NULL) +, m_assoc(NULL) +, m_cfg() { OFStandard::initializeNetwork(); } @@ -45,6 +46,12 @@ DcmSCP::~DcmSCP() dropAndDestroyAssociation(); } + // clean up network structure if initialized + if (m_network) + { + ASC_dropNetwork(&m_network); + } + OFStandard::shutdownNetwork(); } @@ -69,8 +76,16 @@ OFCondition DcmSCP::setConfig(const DcmSCPConfig& config) // ---------------------------------------------------------------------------- -OFCondition DcmSCP::listen() +OFCondition DcmSCP::openListenPort() { + + // clean up network structure if already initialized + if (m_network) + { + ASC_dropNetwork(&m_network); + m_network = NULL; + } + // make sure not to let dcmdata remove trailing blank padding or perform other // manipulations. We want to see the real data. dcmEnableAutomaticInputDataCorrection.set(OFFalse); @@ -98,14 +113,16 @@ OFCondition DcmSCP::listen() return result; // Initialize network, i.e. create an instance of T_ASC_Network*. - T_ASC_Network* network = NULL; - cond = ASC_initializeNetwork(NET_ACCEPTOR, OFstatic_cast(int, m_cfg->getPort()), m_cfg->getACSETimeout(), &network); + cond = ASC_initializeNetwork(NET_ACCEPTOR, OFstatic_cast(int, m_cfg->getPort()), m_cfg->getACSETimeout(), &m_network); if (cond.bad()) + { + m_network = NULL; return cond; + } if (m_cfg->transportLayerEnabled()) { - cond = ASC_setTransportLayer(network, m_cfg->getTransportLayer(), OFFalse /* Do not take over ownership */); + cond = ASC_setTransportLayer(m_network, m_cfg->getTransportLayer(), OFFalse /* Do not take over ownership */); if (cond.bad()) { DCMNET_ERROR("DcmSCP: Error setting secure transport layer: " << cond.text()); @@ -121,7 +138,22 @@ OFCondition DcmSCP::listen() return cond; } - // If we get to this point, the entire initialization process has been completed + return cond; +} + +// ---------------------------------------------------------------------------- + +OFCondition DcmSCP::acceptAssociations() +{ + if (m_network == NULL) + { + DCMNET_ERROR("network port not initialized, call DcmSCP::openListenPort() first."); + return EC_IllegalCall; + } + + OFCondition cond = EC_Normal; + + // At this point, the entire initialization process has been completed // successfully. Now, we want to start handling all incoming requests. Since // this activity is supposed to represent a server process, we do not want to // terminate this activity (unless indicated by the stopAfterCurrentAssociation() @@ -130,7 +162,7 @@ OFCondition DcmSCP::listen() { // Wait for an association and handle the requests of // the calling applications correspondingly. - cond = waitForAssociationRQ(network); + cond = waitForAssociationRQ(m_network); // Check whether we have a timeout if (cond == DUL_NOASSOCIATIONREQUEST) @@ -157,8 +189,8 @@ OFCondition DcmSCP::listen() // Drop the network, i.e. free memory of T_ASC_Network* structure. This call // is the counterpart of ASC_initializeNetwork(...) which was called above. - ASC_dropNetwork(&network); - network = NULL; + ASC_dropNetwork(&m_network); + m_network = NULL; // return ok return cond; @@ -166,6 +198,15 @@ OFCondition DcmSCP::listen() // ---------------------------------------------------------------------------- +OFCondition DcmSCP::listen() +{ + OFCondition cond = openListenPort(); + if (cond.good()) cond = acceptAssociations(); + return cond; +} + +// ---------------------------------------------------------------------------- + void DcmSCP::findPresentationContext(const T_ASC_PresentationContextID presID, OFString& abstractSyntax, OFString& transferSyntax)