Skip to content

Commit

Permalink
Added alternative service provision API to DcmSCP.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Marco Eichelberg committed May 1, 2020
1 parent 5961ce3 commit c3d4c8e
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 11 deletions.
24 changes: 23 additions & 1 deletion dcmnet/include/dcmtk/dcmnet/scp.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 */
/* ************************************************************* */
Expand Down Expand Up @@ -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;

Expand Down
61 changes: 51 additions & 10 deletions dcmnet/libsrc/scp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
// ----------------------------------------------------------------------------

DcmSCP::DcmSCP()
: m_assoc(NULL)
, m_cfg()
: m_network(NULL)
, m_assoc(NULL)
, m_cfg()
{
OFStandard::initializeNetwork();
}
Expand All @@ -45,6 +46,12 @@ DcmSCP::~DcmSCP()
dropAndDestroyAssociation();
}

// clean up network structure if initialized
if (m_network)
{
ASC_dropNetwork(&m_network);
}

OFStandard::shutdownNetwork();
}

Expand All @@ -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);
Expand Down Expand Up @@ -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());
Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -157,15 +189,24 @@ 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;
}

// ----------------------------------------------------------------------------

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)
Expand Down

0 comments on commit c3d4c8e

Please sign in to comment.