/* * * Copyright (C) 1998-2021, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmpstat * * Author: Marco Eichelberg * * Purpose: * classes: DVPSIPCMessage * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #ifdef HAVE_WINDOWS_H // on Windows, we need Winsock2 for network functions #include #endif #include "dcmtk/dcmnet/dcompat.h" #include "dcmtk/dcmpstat/dvpsmsg.h" #include "dcmtk/ofstd/ofstring.h" /* for class OFString */ #include "dcmtk/ofstd/ofbmanip.h" /* for OFBitmanipTemplate<> */ #include "dcmtk/dcmdata/dcswap.h" /* for swapIfNecessary() */ #include "dcmtk/dcmnet/dcmtrans.h" /* for class DcmTransportConnection */ #include "dcmtk/ofstd/ofsockad.h" #include "dcmtk/ofstd/ofstd.h" /* --------------- class DVPSIPCMessage --------------- */ #define PAYLOAD_ALLOCATION_UNIT 1024 #define PAYLOAD_OFFSET 8 // constants for message type const Uint32 DVPSIPCMessage::OK = 0; const Uint32 DVPSIPCMessage::requestApplicationID = 1; const Uint32 DVPSIPCMessage::assignApplicationID = 2; const Uint32 DVPSIPCMessage::applicationTerminates = 3; const Uint32 DVPSIPCMessage::receivedUnencryptedDICOMConnection = 5; const Uint32 DVPSIPCMessage::receivedEncryptedDICOMConnection = 7; const Uint32 DVPSIPCMessage::connectionClosed = 9; const Uint32 DVPSIPCMessage::connectionAborted = 11; const Uint32 DVPSIPCMessage::requestedUnencryptedDICOMConnection = 13; const Uint32 DVPSIPCMessage::requestedEncryptedDICOMConnection = 15; const Uint32 DVPSIPCMessage::receivedDICOMObject = 17; const Uint32 DVPSIPCMessage::sentDICOMObject = 19; // message status constants const Uint32 DVPSIPCMessage::statusOK = 0; const Uint32 DVPSIPCMessage::statusWarning = 1; const Uint32 DVPSIPCMessage::statusError = 2; // client type constants const Uint32 DVPSIPCMessage::clientOther = 0; const Uint32 DVPSIPCMessage::clientStoreSCP = 1; const Uint32 DVPSIPCMessage::clientStoreSCU = 2; const Uint32 DVPSIPCMessage::clientPrintSCP = 3; const Uint32 DVPSIPCMessage::clientPrintSCU = 4; const Uint32 DVPSIPCMessage::clientQRSCP = 5; DVPSIPCMessage::DVPSIPCMessage() : messageType(OK) , payloadUsed(PAYLOAD_OFFSET) , payloadAllocated(PAYLOAD_ALLOCATION_UNIT) , payloadReadOffset(PAYLOAD_OFFSET) , payload(NULL) { payload = new unsigned char[payloadAllocated]; } DVPSIPCMessage::DVPSIPCMessage(const DVPSIPCMessage& copy) : messageType(copy.messageType) , payloadUsed(copy.payloadUsed) , payloadAllocated(copy.payloadAllocated) , payloadReadOffset(copy.payloadReadOffset) , payload(NULL) { payload = new unsigned char[payloadAllocated]; OFBitmanipTemplate::copyMem(copy.payload, payload, payloadUsed); } DVPSIPCMessage::~DVPSIPCMessage() { delete[] payload; } DVPSIPCMessage& DVPSIPCMessage::operator=(const DVPSIPCMessage& copy) { messageType = copy.messageType; payloadUsed = copy.payloadUsed; payloadReadOffset = copy.payloadReadOffset; if (payloadAllocated < payloadUsed) { delete[] payload; payloadAllocated = copy.payloadAllocated; payload = new unsigned char[payloadAllocated]; } OFBitmanipTemplate::copyMem(copy.payload, payload, payloadUsed); return *this; } void DVPSIPCMessage::resizePayload(size_t i) { size_t requiredSize = payloadUsed+i; if (requiredSize < payloadAllocated) return; while (payloadAllocated < requiredSize) payloadAllocated += PAYLOAD_ALLOCATION_UNIT; unsigned char *newpayload = new unsigned char[payloadAllocated]; OFBitmanipTemplate::copyMem(payload, newpayload, payloadUsed); delete[] payload; payload = newpayload; return; } void DVPSIPCMessage::addStringToPayload(const char *str) { size_t length = 0; if (str) length = strlen(str); else str = ""; Uint32 padBytes = 4 - (length % 4); size_t sizeNeeded = sizeof(Uint32)+length+padBytes; resizePayload(sizeNeeded); // write string length addIntToPayload(OFstatic_cast(Uint32, length+padBytes)); // write string OFStandard::strlcpy((char *)(payload + payloadUsed), str, (length+padBytes)); payloadUsed += OFstatic_cast(Uint32, length); // write pad bytes for (Uint32 i=0; i < padBytes; i++) *(payload + payloadUsed++) = 0; return; } void DVPSIPCMessage::addIntToPayload(Uint32 i) { resizePayload(sizeof(Uint32)); unsigned char *target = payload + payloadUsed; // write integer value *(Uint32 *)target = i; // and swap to big endian swapIfNecessary(EBO_BigEndian, gLocalByteOrder, target, sizeof(Uint32), sizeof(Uint32)); payloadUsed += sizeof(Uint32); return; } OFBool DVPSIPCMessage::extractStringFromPayload(OFString& str) { Uint32 length = 0; if (! extractIntFromPayload(length)) return OFFalse; // check if we have sufficient data available if (payloadReadOffset + length > payloadUsed) return OFFalse; str = (const char *)(payload+payloadReadOffset); // guaranteed to be zero terminated string payloadReadOffset += length; return OFTrue; } OFBool DVPSIPCMessage::extractIntFromPayload(Uint32& i) { // check if we have sufficient data available if (payloadReadOffset + sizeof(Uint32) > payloadUsed) return OFFalse; // copy integer into temporary buffer and adjust byte order there unsigned char *temp = new unsigned char[sizeof(Uint32)+8]; // allocate a bit more than needed to be safe OFBitmanipTemplate::copyMem(payload+payloadReadOffset, temp, sizeof(Uint32)); swapIfNecessary(gLocalByteOrder, EBO_BigEndian, temp, sizeof(Uint32), sizeof(Uint32)); payloadReadOffset += sizeof(Uint32); i = *(Uint32 *)temp; delete[] temp; return OFTrue; } void DVPSIPCMessage::rewindPayload() { payloadReadOffset = PAYLOAD_OFFSET; } void DVPSIPCMessage::erasePayload() { payloadUsed = PAYLOAD_OFFSET; } OFBool DVPSIPCMessage::send(DcmTransportConnection &connection) { // adjust message type and length *(Uint32 *)payload = messageType; *(Uint32 *)(payload + sizeof(Uint32)) = (payloadUsed - PAYLOAD_OFFSET); swapIfNecessary(EBO_BigEndian, gLocalByteOrder, payload, 2*sizeof(Uint32), sizeof(Uint32)); // send if (connection.write(payload, (size_t)payloadUsed) <= 0) return OFFalse; return OFTrue; } OFBool DVPSIPCMessage::receive(DcmTransportConnection &connection) { payloadReadOffset = PAYLOAD_OFFSET; // read message type and payload length if (connection.read(payload, 2*sizeof(Uint32)) <= 0) return OFFalse; swapIfNecessary(gLocalByteOrder, EBO_BigEndian, payload, 2*sizeof(Uint32), sizeof(Uint32)); messageType = *(Uint32 *)payload; payloadUsed = *(Uint32 *)(payload+sizeof(Uint32)); // check if we need to allocate more memory Uint32 requiredSize = payloadUsed + PAYLOAD_OFFSET; if (requiredSize > payloadAllocated) { delete[] payload; while (payloadAllocated < requiredSize) payloadAllocated += PAYLOAD_ALLOCATION_UNIT; payload = new unsigned char[payloadAllocated]; } // read payload if any if (payloadUsed > 0) { if (connection.read(payload+PAYLOAD_OFFSET, (size_t)payloadUsed) <= 0) { payloadUsed = PAYLOAD_OFFSET; return OFFalse; } else { payloadUsed += PAYLOAD_OFFSET; return OFTrue; } } else payloadUsed = PAYLOAD_OFFSET; return OFTrue; } /* --------------- class DVPSIPCClient --------------- */ DVPSIPCClient::DVPSIPCClient(Uint32 clientType, const char *txt, int thePort, OFBool keepOpen) : port(thePort) , serverActive(OFTrue) , applicationID(0) , keepConnectionOpen(keepOpen) , connection(NULL) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::requestApplicationID); msg.addIntToPayload(clientType); msg.addIntToPayload(DVPSIPCMessage::statusOK); msg.addStringToPayload(txt); if (performTransaction(msg)) { if ((msg.getMessageType() != DVPSIPCMessage::assignApplicationID) || (! msg.extractIntFromPayload(applicationID))) { // protocol violation serverActive = OFFalse; } } else { serverActive = OFFalse; } return; } DVPSIPCClient::~DVPSIPCClient() { if (connection) { connection->close(); delete connection; } } void DVPSIPCClient::requestConnection() { if (connection) return; // connection already open #ifdef _WIN32 SOCKET s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) return; #else int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return; #endif OFSockAddr server; OFStandard::getAddressByHostname("localhost", server); server.setPort(OFstatic_cast(unsigned short, htons(OFstatic_cast(unsigned short, port)))); if (connect(s, server.getSockaddr(), server.size()) < 0) { #ifdef HAVE_WINSOCK_H (void) shutdown(s, 1 /* SD_SEND */); (void) closesocket(s); #else (void) close(s); #endif return; } connection = new DcmTCPConnection(s); } OFBool DVPSIPCClient::performTransaction(DVPSIPCMessage& msg) { if (!serverActive) return OFFalse; requestConnection(); // this would be the right place to retry connections if (connection == NULL) return OFFalse; OFBool result = msg.send(*connection); if (result) result = msg.receive(*connection); if (! keepConnectionOpen) { connection->close(); delete connection; connection = NULL; } return result; } void DVPSIPCClient::notifyApplicationTerminates(Uint32 status) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::applicationTerminates); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); performTransaction(msg); } void DVPSIPCClient::notifyReceivedUnencryptedDICOMConnection(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::receivedUnencryptedDICOMConnection); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); } void DVPSIPCClient::notifyReceivedEncryptedDICOMConnection(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::receivedEncryptedDICOMConnection); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); } void DVPSIPCClient::notifyConnectionClosed(Uint32 status) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::connectionClosed); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); performTransaction(msg); } void DVPSIPCClient::notifyConnectionAborted(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::connectionAborted); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); } void DVPSIPCClient::notifyRequestedUnencryptedDICOMConnection(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::requestedUnencryptedDICOMConnection); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); } void DVPSIPCClient::notifyRequestedEncryptedDICOMConnection(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::requestedEncryptedDICOMConnection); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); } void DVPSIPCClient::notifyReceivedDICOMObject(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::receivedDICOMObject); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); } void DVPSIPCClient::notifySentDICOMObject(Uint32 status, const char *txt) { DVPSIPCMessage msg; msg.setMessageType(DVPSIPCMessage::sentDICOMObject); msg.addIntToPayload(applicationID); msg.addIntToPayload(status); msg.addStringToPayload(txt); performTransaction(msg); }