#include "DicSrv.h" #include "io.h" #define PROTOCOL_VERSION 1 #define IMPLEMENTATION_CLASS_UID "1.2.840.114257.1123456" #define IMPLEMENTATION_VERSION_NAME "1" L_VOID SetServerCallbacks(HWND hDlg) { DICOMNETCALLBACK cb; memset(&cb, 0, sizeof(DICOMNETCALLBACK)); cb.pUserData = (L_VOID L_FAR*)hDlg; cb.pfnAccept = AcceptCallback; cb.pfnClose = CloseCallback; L_DicomSetCallback(ghServer, &cb); } L_VOID SetClientCallbacks(HWND hDlg, HDICOMNET hClient) { DICOMNETCALLBACK cb; memset(&cb, 0, sizeof(DICOMNETCALLBACK)); cb.pUserData = (L_VOID L_FAR*)hDlg; cb.pfnConnect = ConnectCallback; cb.pfnReceive = ReceiveCallback; cb.pfnReceiveData = ReceiveDataCallback; cb.pfnReceiveAssociateRequest = ReceiveAssociateRequestCallback; cb.pfnReceiveAssociateAccept = ReceiveAssociateAcceptCallback; cb.pfnReceiveAssociateReject = ReceiveAssociateRejectCallback; cb.pfnReceiveReleaseRequest = ReceiveReleaseRequestCallback; cb.pfnReceiveReleaseResponse = ReceiveReleaseResponseCallback; cb.pfnReceiveAbort = ReceiveAbortCallback; cb.pfnClose = CloseClientCallback; cb.pfnReceiveCStoreRequest = ReceiveCStoreRequestCallback; cb.pfnReceiveCStoreResponse = ReceiveCStoreResponseCallback; cb.pfnReceiveCFindRequest = ReceiveCFindRequestCallback; cb.pfnReceiveCMoveRequest = ReceiveCMoveRequestCallback; cb.pfnReceiveCEchoRequest = ReceiveCEchoRequestCallback; cb.pfnReceiveUnknown = ReceiveUnknownCallback; cb.pfnOnSecureLinkReady = exOnSecureLinkReady; cb.pfnGetChallenge = exGetChallengeISCL; cb.pfnInternalAuthenticate = exInternalAuthenticateISCL; cb.pfnExternalAuthenticate = exExternalAuthenticateISCL; cb.pfnOnReceivedISCLPacket = exOnReceivedISCLPacket; L_DicomSetCallback(hClient, &cb); } L_INT L_EXPORT EXT_CALLBACK exPrivateKeyPassword(HDICOMNET hNet, L_CHAR *pszPassword, L_INT nSize, L_INT rwFlag, L_VOID *pUserData) { strcpy(pszPassword, gszPrivateKeyPassword); return strlen(gszPrivateKeyPassword); } L_VOID SetCallbacksExt(HDICOMNET hNet) { DICOMNETCALLBACKEXT cb; memset(&cb, 0, sizeof(cb)); cb.uStructSize = sizeof(cb); cb.pfnPrivateKeyPassword = exPrivateKeyPassword; L_DicomSetCallbackExt(hNet, &cb); } ///////////////////// Server /////////////////////////////////// L_VOID L_EXPORT EXT_CALLBACK AcceptCallback(HDICOMNET hNet, L_INT nError, L_VOID* pUserData) { HWND hDlg, hConnectionsCtrl; HDICOMNET hClient; L_CHAR szPeerAddress[64], szDate[64], szOut[256]; HTREEITEM hItem, hChild; SYSTEMTIME st; TV_INSERTSTRUCT tvis; pCLIENTDATA pClientData; int i; int nValidClient = DICOM_SUCCESS; if (nError != DICOM_SUCCESS) return; hDlg = (HWND) pUserData; hConnectionsCtrl = GetDlgItem(hDlg, IDC_TREE3); switch (gSecureMode) { case DICOM_SECURE_NONE: hClient = L_DicomCreateNet(gszTempFilesFolder, DICOM_SECURE_NONE); if (!hClient) LogEvent(hDlg, "", "Connection Rejected - Out of Memory"); break; case DICOM_SECURE_ISCL: if (LoadFromFileSettings() == FALSE) { LoadDefaultSettings(); } hClient = L_DicomCreateNet(gszTempFilesFolder, DICOM_SECURE_ISCL); if (!hClient) { LogEvent(hDlg, "", "Connection Rejected - Out of Memory"); break; } L_DicomSetMutualAuthAlgISCL(hClient, gISCLAuthAlg); for (i = 0; i < 8; i++) { if (gISCLAuthKeys[i] != 0) { L_DicomSetMutualAuthKeyISCL(hClient, i + 1, gISCLAuthKeys[i]); } } L_DicomSetIndexForMutualAuthISCL(hClient, gISCLAuthCurrentKey+1); L_DicomSetDefaultEncryptionISCL(hClient, gISCLEncAlg); L_DicomSetDefaultSigningISCL(hClient, gISCLEncSign); for (i = 0; i < 8; i++) { if (gISCLEncKeys[i] != 0) { L_DicomSetEncryptKeyISCL(hClient, i + 1, gISCLEncKeys[i]); } } L_DicomSetIndexForEncryptISCL(hClient, gISCLEncCurrentKey + 1); L_DicomSetMaxMessageLengthISCL(hClient, 1024000); L_DicomSetMaxCommBlockLengthISCL(hClient, 8129); L_DicomSetAuthDataISCL(hClient,SERVER_AUTH_DATA, SERVER_AUTH_DATA_LEN); break; case DICOM_SECURE_TLS: { L_INT nRet = DICOM_SUCCESS; char szCACertName[_MAX_PATH]; char szServerCertName[_MAX_PATH]; L_SSL_CTX_CREATE ctxCreate; memset(&ctxCreate, 0, sizeof(L_SSL_CTX_CREATE)); ctxCreate.uStructSize = sizeof(L_SSL_CTX_CREATE); ctxCreate.uFlags = FLAG_SSL_CTX_CREATE_METHOD_TYPE | FLAG_SSL_CTX_CREATE_VERIFY_MODE | FLAG_SSL_CTX_CREATE_VERIFY_DEPTH | FLAG_SSL_CTX_CREATE_OPTIONS; ctxCreate.nMethodTypeSSL= TYPE_SSLV23_METHOD; GetCertName(CA_CERT_NAME, szCACertName); if (_access(szCACertName, 4)==0) { ctxCreate.pszCAfile = szCACertName; ctxCreate.uFlags |= FLAG_SSL_CTX_CREATE_CAFILE; } ctxCreate.uVerifyMode = L_SSL_VERIFY_PEER | L_SSL_VERIFY_FAIL_IF_NO_PEER_CERT; ctxCreate.nVerifyDepth = 9; ctxCreate.nOptions = L_SSL_OP_NO_SSLv2|L_SSL_OP_ALL; ctxCreate.nReserved1 = 0; ctxCreate.nReserved2 = 0; hClient = L_DicomCreateNetExt(gszTempFilesFolder, DICOM_SECURE_TLS, &ctxCreate); if (hClient) { L_DicomSetCipherToIndexTLS(hClient, 0, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA); // Set password callback for certificate private key // The password callback returns the password (in this case, 'test') SetCallbacksExt(hClient); GetCertName(SERVER_CERT_NAME, szServerCertName); nRet = L_DicomSetClientCertificateTLS(hClient, szServerCertName, L_TLS_FILETYPE_PEM, NULL); if (nRet != DICOM_SUCCESS) { LogEvent(hDlg, "", "Invalid Server Certificate"); return; } } nValidClient = ctxCreate.nSuccess; } break; } if (!hClient) { return; } SetClientCallbacks(hDlg, hClient); pClientData = (pCLIENTDATA) malloc(sizeof(CLIENTDATA)); if (!pClientData) { L_DicomFreeNet(hClient); LogEvent(hDlg, "", "Connection Rejected - Out of Memory"); return; } // Accept the connection attempt nError = L_DicomAccept(ghServer, hClient); if (nError != DICOM_SUCCESS) { L_DicomFreeNet(hClient); free(pClientData); LogEvent(hDlg, "", "Connection Rejected - Error during Accept"); return; } if (nValidClient != DICOM_SUCCESS) { L_CHAR *pszError = NULL; L_CHAR szMsg[200]; pszError = GetTLSErrorString(nValidClient); // close the connection wsprintf(szMsg, "[%d]%s -- closing connection", nError, pszError); LogEvent(hDlg, "", szMsg); L_DicomClose(hClient); L_DicomFreeNet(hClient); return; } memset(szPeerAddress, 0, sizeof(szPeerAddress)); L_DicomGetPeerInfo(hClient, szPeerAddress, NULL); if (L_DicomGetClientCount(ghServer) > (L_UINT32) giMaxClients) // Too many connections! { L_DicomClose(hClient); L_DicomFreeNet(hClient); free(pClientData); LogEvent(hDlg, szPeerAddress, "Connection Rejected - Max Connections Reached"); return; } if (!FindClient(hDlg, szPeerAddress, NULL, NULL)) { // We don't know this user; close the connection L_DicomClose(hClient); L_DicomFreeNet(hClient); free(pClientData); LogEvent(hDlg, szPeerAddress, "Connection Rejected - Unknown User"); return; } LogEvent(hDlg, szPeerAddress, "Connection Accepted"); // Display some information about the connection: GetLocalTime(&st); wsprintf(szDate, "%hu/%hu/%hu %hu:%hu:%hu %s", st.wMonth, st.wDay, st.wYear, (st.wHour <= 12) ? st.wHour : st.wHour - 12, st.wMinute, st.wSecond, (st.wHour >= 12) ? "PM" : "AM"); wsprintf(szOut, "0x%X", hClient); memset(pClientData, 0, sizeof(CLIENTDATA)); pClientData->hClient = hClient; memset(&tvis, 0, sizeof(TV_INSERTSTRUCT)); tvis.hParent = NULL; tvis.hInsertAfter = TVI_LAST; tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM; tvis.item.pszText = szOut; tvis.item.iImage = 0; tvis.item.iSelectedImage = 0; tvis.item.lParam = (LPARAM) pClientData; hItem = TreeView_InsertItem(hConnectionsCtrl, &tvis); lstrcpy(szOut, "Remote AE: "); tvis.hParent = hItem; tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT; tvis.item.pszText = szOut; tvis.item.iImage = 2; tvis.item.iSelectedImage = 2; hChild = TreeView_InsertItem(hConnectionsCtrl, &tvis); wsprintf(szOut, "Remote Host: %s", szPeerAddress); tvis.hParent = hItem; tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT; tvis.item.pszText = szOut; tvis.item.iImage = 2; tvis.item.iSelectedImage = 2; hChild = TreeView_InsertItem(hConnectionsCtrl, &tvis); wsprintf(szOut, "Connection Time: %s", szDate); tvis.hParent = hItem; tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT; tvis.item.pszText = szOut; tvis.item.iImage = 2; tvis.item.iSelectedImage = 2; hChild = TreeView_InsertItem(hConnectionsCtrl, &tvis); lstrcpy(szOut, "Number of Messages: 0"); tvis.hParent = hItem; tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM; tvis.item.pszText = szOut; tvis.item.iImage = 2; tvis.item.iSelectedImage = 2; tvis.item.lParam = 0L; hChild = TreeView_InsertItem(hConnectionsCtrl, &tvis); lstrcpy(szOut, "Last Action: Connect"); tvis.hParent = hItem; tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT; tvis.item.pszText = szOut; tvis.item.iImage = 2; tvis.item.iSelectedImage = 2; hChild = TreeView_InsertItem(hConnectionsCtrl, &tvis); if (L_DicomGetClientCount(ghServer) == 1) { SetTimer(hDlg, CLIENTS_TIMER_ID, 1000, NULL); } } L_VOID L_EXPORT EXT_CALLBACK CloseCallback(HDICOMNET hNet, L_INT nError, HDICOMNET hPeer, L_VOID* pUserData) { HWND hDlg, hConnectionsCtrl; HTREEITEM hItem; TV_ITEM item; pCLIENTDATA pClientData; hDlg = (HWND) pUserData; hConnectionsCtrl = GetDlgItem(hDlg, IDC_TREE3); // A client has closed hItem = TreeView_GetRoot(hConnectionsCtrl); while (hItem) { memset(&item, 0, sizeof(TV_ITEM)); item.mask = TVIF_HANDLE | TVIF_PARAM; item.hItem = hItem; TreeView_GetItem(hConnectionsCtrl, &item); pClientData = (pCLIENTDATA) item.lParam; if (pClientData->hClient == hPeer) { TreeView_DeleteItem(hConnectionsCtrl, hItem); L_DicomFreeNet(hPeer); free(pClientData); break; } hItem = TreeView_GetNextItem(hConnectionsCtrl, hItem, TVGN_NEXT); } } ///////////////////// Client /////////////////////////////////// L_VOID L_EXPORT EXT_CALLBACK ReceiveAssociateRequestCallback(HDICOMNET hNet, HDICOMPDU hPDU, L_VOID* pUserData) { HWND hDlg; L_CHAR* pszCallingAETitle, * pszCalledAETitle; L_CHAR szIPAddress[200]; HDICOMPDU hAssociate; L_INT nPresContCount; L_UCHAR nPresContID; L_CHAR* pszAbstractSynName, * pszTransSynName; L_INT nTransSynCount; int i, j; hDlg = (HWND) pUserData; pszCallingAETitle = L_DicomGetCalling(hPDU); // We have received an Associate Request LogEvent(hDlg, pszCallingAETitle, "Associate Request received"); // Reject based on the Called AE Title pszCalledAETitle = L_DicomGetCalled(hPDU); if (lstrcmp(pszCalledAETitle, gszServerAETitle) != 0) { L_DicomSendAssociateReject(hNet, PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_USER, PDU_REJECT_REASON_CALLED); LogEvent(hDlg, pszCallingAETitle, "Associate Reject sent - invalid Called AE Title"); return; } // Reject based on the Calling AE Title L_DicomGetPeerInfo(hNet, szIPAddress, NULL); if (!FindClient(hDlg, szIPAddress, pszCallingAETitle, NULL)) { L_DicomSendAssociateReject(hNet, PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_USER, PDU_REJECT_REASON_CALLING); LogEvent(hDlg, pszCallingAETitle, "Associate Reject sent - Invalid Calling AE Title"); return; } SetClientInfo(hDlg, hNet, szIPAddress, pszCallingAETitle); /* Send the Associate response */ hAssociate = L_DicomCreateAssociate(FALSE); L_DicomResetAssociate(hAssociate, FALSE); // The Protocol Version L_DicomSetVersion(hAssociate, PROTOCOL_VERSION); // Titles of the Called and Calling AEs L_DicomSetCalled(hAssociate, gszServerAETitle); L_DicomSetCalling(hAssociate, pszCallingAETitle); // The Application Context Name L_DicomSetApplication(hAssociate, UID_APPLICATION_CONTEXT_NAME); nPresContCount = L_DicomGetPresentationCount(hPDU); // Presentation Contexts for (i = 0; i < nPresContCount; i++) { nPresContID = L_DicomGetPresentation(hPDU, i); pszAbstractSynName = L_DicomGetAbstract(hPDU, nPresContID); L_DicomAddPresentation(hAssociate, nPresContID, PDU_ACCEPT_RESULT_SUCCESS, pszAbstractSynName); // Do we support the Abstract Syntax of this Presentation Context? if (L_DicomFindUID(pszAbstractSynName)) { nTransSynCount = L_DicomGetTransferCount(hPDU, nPresContID); for (j = 0; j < nTransSynCount; j++) { pszTransSynName = L_DicomGetTransfer(hPDU, nPresContID, j); // Select this Transfer Syntax if we support it if (L_DicomFindUID(pszTransSynName)) { L_DicomAddTransfer(hAssociate, nPresContID, pszTransSynName); break; } } // Role Selection L_DicomSetRoleSelect(hAssociate, nPresContID, L_DicomIsRoleSelect(hPDU, nPresContID), PDU_ROLE_SUPPORT, PDU_ROLE_SUPPORT); // Reject this Presentation Context if we didn't find a suitable Transfer Syntax if (j >= nTransSynCount) { L_DicomSetResult(hAssociate, nPresContID, PDU_ACCEPT_RESULT_TRANSFER_SYNTAX); } } else { // Reject this Presentation Context because we don't support the Abstract Syntax L_DicomSetResult(hAssociate, nPresContID, PDU_ACCEPT_RESULT_ABSTRACT_SYNTAX); } } // Maximum Length Application PDU if (L_DicomIsMaxLength(hPDU)) { L_DicomSetMaxLength(hAssociate, TRUE, L_DicomGetMaxLength(hPDU)); } // Implementation Class UID L_DicomSetImplementClass(hAssociate, TRUE, IMPLEMENTATION_CLASS_UID); // Implementation Version Name L_DicomSetImplementVersion(hAssociate, TRUE, IMPLEMENTATION_VERSION_NAME); // Send the response L_DicomSendAssociateAccept(hNet, hAssociate); LogEvent(hDlg, pszCallingAETitle, "Associate Accept sent"); EnableClientTimeout(hDlg, hNet, TRUE); DisplayAssociate(hDlg, hNet, hAssociate); LastClientAction(hDlg, hNet, "Associated", FALSE); } L_VOID L_EXPORT EXT_CALLBACK ReceiveReleaseRequestCallback(HDICOMNET hNet, L_VOID* pUserData) { HWND hDlg; char szAETitle[20] = ""; hDlg = (HWND) pUserData; EnableClientTimeout(hDlg, hNet, FALSE); // We have received a Release Request GetClientInfo(hDlg, hNet, szAETitle); LogEvent(hDlg, szAETitle, "Release Request received"); // Send Release Response L_DicomSendReleaseResponse(hNet); EnableClientTimeout(hDlg, hNet, TRUE); LastClientAction(hDlg, hNet, "Association Released", FALSE); } L_VOID L_EXPORT EXT_CALLBACK CloseClientCallback(HDICOMNET hNet, L_INT nError, HDICOMNET hPeer, L_VOID *pUserData) { if ((ghClient == hNet) && (gnMatchIndex < gnMatchesCount)) { L_DicomSendCMoveResponse( ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); } } L_VOID L_EXPORT EXT_CALLBACK ReceiveAbortCallback(HDICOMNET hNet, L_UCHAR nSource, L_UCHAR nReason, L_VOID* pUserData) { HWND hDlg = (HWND) pUserData; // Are we processing a C-MOVE-RQ? if (L_DicomIsConnected(ghClient) && ghMoveNet == hNet) { KillTimer(hDlg, MOVE_TIMER_ID); if (L_DicomIsAssociated(ghClient)) { L_DicomSendAbort(ghClient, nSource, nReason); L_DicomClose(ghMoveNet); } else { L_DicomClose(ghMoveNet); L_DicomClose(ghClient); } } else // we received an Abort on the SCU which is storing images if (ghClient == hNet) { L_DicomSendCMoveResponse(ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); L_DicomClose(ghClient); } else { L_DicomClose(hNet); } // We have received an Abort LogEvent(hDlg, L_DicomGetCalling(L_DicomGetAssociate(hNet)), "Abort received"); LastClientAction(hDlg, hNet, "Association Aborted", FALSE); } L_VOID L_EXPORT EXT_CALLBACK ReceiveReleaseResponseCallback(HDICOMNET hNet, L_VOID* pUserData) { HWND hDlg = (HWND) pUserData; L_DicomClose(hNet); // We have received a Release Response LogEvent(hDlg, gszServerAETitle, "Release Response received"); } L_VOID L_EXPORT EXT_CALLBACK ReceiveAssociateAcceptCallback(HDICOMNET hNet, HDICOMPDU hPDU, L_VOID* pUserData) { HWND hDlg; if (!gpMoveMatches) return; hDlg = (HWND) pUserData; KillTimer(hDlg, MOVE_TIMER_ID); LogEvent(hDlg, gszServerAETitle, "Associate Accept received"); if (!SendMatchedInstance(hDlg, hNet, NULL)) { free(gpMoveMatches); gpMoveMatches = NULL; gnMatchesCount = 0; L_DicomSendReleaseRequest(hNet); LogEvent(hDlg, gszServerAETitle, "Release Request sent"); L_DicomSendCMoveResponse(ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); LogEvent(hDlg, L_DicomGetCalling(L_DicomGetAssociate(ghMoveNet)), "C-MOVE-RESPONSE sent"); } } L_VOID L_EXPORT EXT_CALLBACK ReceiveAssociateRejectCallback(HDICOMNET hNet, L_UCHAR nResult, L_UCHAR nSource, L_UCHAR nReason, L_VOID* pUserData) { HWND hDlg; if (!gpMoveMatches) return; free(gpMoveMatches); gpMoveMatches = NULL; gnMatchesCount = 0; hDlg = (HWND) pUserData; KillTimer(hDlg, MOVE_TIMER_ID); LogEvent(hDlg, gszServerAETitle, "Associate Reject received"); L_DicomClose(hNet); L_DicomSendCMoveResponse(ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); LogEvent(hDlg, L_DicomGetCalling(L_DicomGetAssociate(ghMoveNet)), "C-MOVE-RESPONSE sent"); } L_VOID L_EXPORT EXT_CALLBACK ReceiveCEchoRequestCallback(HDICOMNET hNet, L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR L_FAR *pszClass, L_VOID *pUserData) { HWND hDlg; L_UINT16 nStatus; HDICOMPDU hPDU; L_UCHAR nID; L_CHAR* pszUser; pDICOMUID pUID; L_CHAR szOut[1024]; hPDU = L_DicomGetAssociate(hNet); if (!hPDU) { // The connection is not associated, so ignore the message return; } hDlg = (HWND) pUserData; EnableClientTimeout(hDlg, hNet, FALSE); // We have received a C-ECHO-REQUEST pszUser = L_DicomGetCalling(hPDU); LogEvent(hDlg, pszUser, "C-ECHO-REQUEST received"); LastClientAction(hDlg, hNet, "C-ECHO-REQUEST", TRUE); pUID = L_DicomFindUID(pszClass); if (!pUID) nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; else { nID = L_DicomFindAbstract(hPDU, pszClass); if ((nID == 0) || (L_DicomGetResult(hPDU, nID) != PDU_ACCEPT_RESULT_SUCCESS)) { wsprintf(szOut, "C-ECHO-REQUEST - Abstract Syntax, %s, Not Supported by Association!", pUID->pszName); LogEvent(hDlg, pszUser, szOut); nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else nStatus = COMMAND_STATUS_SUCCESS; } L_DicomSendCEchoResponse(hNet, nPresentationID, nMessageID, pszClass, nStatus); LogEvent(hDlg, pszUser, "C-ECHO-RESPONSE sent"); EnableClientTimeout(hDlg, hNet, TRUE); } L_VOID L_EXPORT EXT_CALLBACK ReceiveUnknownCallback(HDICOMNET hNet, L_UCHAR nPresentationID, HDICOMDS hCS, HDICOMDS hDS, L_VOID *pUserData) { HDICOMPDU hPDU = L_DicomGetAssociate(hNet); HWND hDlg = (HWND) pUserData; if (!hPDU) { // The connection is not associated, so ignore the message return; } L_DicomSendAbort(hNet, PDU_ABORT_SOURCE_PROVIDER, PDU_ABORT_REASON_UNKNOWN); LogEvent(hDlg, L_DicomGetCalling(hPDU), "Unknown data received, sending Abort."); } L_VOID L_EXPORT EXT_CALLBACK ReceiveCStoreRequestCallback(HDICOMNET hNet, L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR * pszClass, L_CHAR * pszInstance, L_UINT16 nPriority, L_CHAR L_FAR *pszMoveAE, L_UINT16 nMoveMessageID, HDICOMDS hDS, L_VOID* pUserData) { HWND hDlg; L_UINT16 nStatus; L_UCHAR nID; L_CHAR* pszUser, * pszMessage, * pszSOPInstanceUID; L_CHAR szOut[1024], szFile[MAX_PATH], szFilename[MAX_PATH]; HDICOMPDU hPDU; pDICOMUID pUID; pDICOMELEMENT pElement; int iRet; hPDU = L_DicomGetAssociate(hNet); if (!hPDU) { // The connection is not associated, so ignore the message return; } hDlg = (HWND) pUserData; EnableClientTimeout(hDlg, hNet, FALSE); // We have received a C-STORE-REQUEST lstrcpy(szOut, "C-STORE-REQUEST received"); if (lstrlen(gszLastLoggedDSFileName)) { wsprintf(szOut, "%s and logged into: %s", szOut, gszLastLoggedDSFileName); } pszUser = L_DicomGetCalling(hPDU); LogEvent(hDlg, pszUser, szOut); LastClientAction(hDlg, hNet, "C-STORE-REQUEST", TRUE); pUID = L_DicomFindUID(pszClass); if (!pUID) nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; else { nID = L_DicomFindAbstract(hPDU, pszClass); if ((nID == 0) || (L_DicomGetResult(hPDU, nID) != PDU_ACCEPT_RESULT_SUCCESS)) { wsprintf(szOut, "C-STORE-REQUEST - Abstract Syntax, %s, Not Supported by Association!", pUID->pszName); LogEvent(hDlg, pszUser, szOut); nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else nStatus = COMMAND_STATUS_SUCCESS; } // Save the SOP Instance if (nStatus == COMMAND_STATUS_SUCCESS) { gbImport = FALSE; nStatus = COMMAND_STATUS_PROCESSING_FAILURE; pszMessage = "Error saving DICOM Data Set"; pElement = L_DicomFindFirstElement(hDS, NULL, TAG_SOP_INSTANCE_UID, FALSE); if (pElement) { pszSOPInstanceUID = L_DicomGetStringValue(hDS, pElement, 0, 1); if (lstrlen(pszSOPInstanceUID)) { wsprintf(szFile, "%s.dcm", pszSOPInstanceUID); iRet = InsertDataSet(hDlg, hDS, szFile); if (iRet == 2) // Already exists? { pszMessage = "Data Set already exists in Database!"; } else if (iRet == 0) { wsprintf(szFilename, "%s%s", gszImagesFolder, szFile); if (SaveDataSet(hNet, hDS, szFilename)) { nStatus = COMMAND_STATUS_SUCCESS; InitServerDS(hDlg); } } } L_DicomFreeValue(hDS, pElement); } if (nStatus != COMMAND_STATUS_SUCCESS) { LogEvent(hDlg, pszUser, pszMessage); } // Refresh the DICOM Directory display DisplayDICOMDir(hDlg); } L_DicomSendCStoreResponse(hNet, nPresentationID, nMessageID, pszClass, pszInstance, nStatus); LogEvent(hDlg, pszUser, "C-STORE-RESPONSE sent"); EnableClientTimeout(hDlg, hNet, TRUE); } L_VOID L_EXPORT EXT_CALLBACK ReceiveCStoreResponseCallback(HDICOMNET hNet, L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR L_FAR *pszClass, L_CHAR L_FAR *pszInstance, L_UINT16 nStatus, L_VOID* pUserData) { HWND hDlg; char szFailedSOPInstanceUID[UID_MAX_SIZE + 1]; if (!gpMoveMatches) return; hDlg = (HWND) pUserData; KillTimer(hDlg, MOVE_TIMER_ID); LogEvent(hDlg, gszServerAETitle, "C-STORE-RESPONSE received"); switch (nStatus) { case COMMAND_STATUS_SUCCESS: gnSuccessfulSubOperations++; break; case COMMAND_STATUS_WARNING: case 0xB007: case 0xB006: gnWarningSubOperations++; break; default: gnFailedSubOperations++; } if (gnMatchIndex < gnMatchesCount - 1) { L_DicomSendCMoveResponse(ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_PENDING, (L_UINT16) (gnMatchesCount - gnMatchIndex - 1), (L_UINT16) gnSuccessfulSubOperations, (L_UINT16) gnFailedSubOperations, (L_UINT16) gnWarningSubOperations, NULL); LogEvent(hDlg, L_DicomGetCalling(L_DicomGetAssociate(ghMoveNet)), "C-MOVE-RESPONSE sent"); if (!SendMatchedInstance(hDlg, hNet, szFailedSOPInstanceUID)) { ReceiveCStoreResponseCallback(hNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, szFailedSOPInstanceUID, COMMAND_STATUS_PROCESSING_FAILURE, pUserData); } } else { free(gpMoveMatches); gpMoveMatches = NULL; gnMatchesCount = 0; L_DicomSendReleaseRequest(hNet); LogEvent(hDlg, gszServerAETitle, "Release Request sent"); // Done! L_DicomSendCMoveResponse(ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_SUCCESS, 0, (L_UINT16) gnSuccessfulSubOperations, (L_UINT16) gnFailedSubOperations, (L_UINT16) gnWarningSubOperations, NULL); LogEvent(hDlg, L_DicomGetCalling(L_DicomGetAssociate(ghMoveNet)), "C-MOVE-RESPONSE sent"); } } L_VOID L_EXPORT EXT_CALLBACK ReceiveCFindRequestCallback(HDICOMNET hNet, L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR L_FAR *pszClass, L_UINT16 nPriority, HDICOMDS hDS, L_VOID* pUserData) { HWND hDlg; HDICOMPDU hPDU; L_CHAR* pszUser; L_UINT16 uStatus; pDICOMUID pUID; L_UCHAR nID; L_CHAR szOut[1024]; pDICOMELEMENT pElement; L_CHAR* pszQRLevel; hPDU = L_DicomGetAssociate(hNet); if (!hPDU) { // The connection is not associated, so ignore the message return; } hDlg = (HWND) pUserData; EnableClientTimeout(hDlg, hNet, FALSE); // We have received a C-FIND-RQ lstrcpy(szOut, "C-FIND-REQUEST received"); if (lstrlen(gszLastLoggedDSFileName)) { wsprintf(szOut, "%s and logged into: ", szOut, gszLastLoggedDSFileName); } pszUser = L_DicomGetCalling(hPDU); LogEvent(hDlg, pszUser, szOut); LastClientAction(hDlg, hNet, "C-FIND-REQUEST", TRUE); uStatus = COMMAND_STATUS_SUCCESS; pUID = L_DicomFindUID(pszClass); if (!pUID) { uStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else { nID = L_DicomFindAbstract(hPDU, pszClass); if (nID == 0 || L_DicomGetResult(hPDU, nID) != PDU_ACCEPT_RESULT_SUCCESS) { uStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; wsprintf(szOut, "C-FIND-REQUEST - Abstract Syntax, %s, Not Supported by Association!", pUID->pszName); LogEvent(hDlg, pszUser, szOut); } } if (uStatus == COMMAND_STATUS_SUCCESS) { if (hDS) { // Get the Query/Retrieve Level pElement = L_DicomFindFirstElement(hDS, NULL, TAG_QUERY_RETRIEVE_LEVEL, FALSE); if (pElement) { pszQRLevel = L_DicomGetStringValue(hDS, pElement, 0, 1); if (lstrcmp(pszClass, UID_PATIENT_ROOT_QUERY_FIND) == 0) { if (lstrcmpi(pszQRLevel, "PATIENT") == 0) { FindPatients(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "STUDY") == 0) { FindStudies(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser, TRUE); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "SERIES") == 0) { FindSeries(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser, TRUE); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "IMAGE") == 0) { FindImages(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser, TRUE); EnableClientTimeout(hDlg, hNet, TRUE); return; } else { uStatus = lstrlen(pszQRLevel) ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else if (lstrcmp(pszClass, UID_STUDY_ROOT_QUERY_FIND) == 0) { if (lstrcmpi(pszQRLevel, "STUDY") == 0) { FindStudies(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser, FALSE); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "SERIES") == 0) { FindSeries(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser, FALSE); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "IMAGE") == 0) { FindImages(hDlg, hNet, hDS, nPresentationID, nMessageID, pszClass, pszUser, FALSE); EnableClientTimeout(hDlg, hNet, TRUE); return; } else { uStatus = lstrlen(pszQRLevel) ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else { uStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } else { uStatus = COMMAND_STATUS_MISSING_ATTRIBUTE; } } else { uStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } L_DicomSendCFindResponse(hNet, nPresentationID, nMessageID, pszClass, uStatus, NULL); LogEvent(hDlg, pszUser, "C-FIND-RESPONSE sent"); EnableClientTimeout(hDlg, hNet, TRUE); } L_VOID L_EXPORT EXT_CALLBACK ReceiveCMoveRequestCallback(HDICOMNET hNet, L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR L_FAR *pszClass, L_UINT16 nPriority, L_CHAR L_FAR *pszMoveAE, HDICOMDS hDS, L_VOID* pUserData) { HDICOMPDU hPDU; HWND hDlg, hUserListCtrl; L_CHAR* pszUser, * pszQRLevel; char szBuffer[512], szMoveDstIP[64]; L_UINT16 nStatus; pDICOMUID pUID; L_UCHAR nID; L_UINT uMoveDstPort; int iClientsCount, iClientIndex; pDICOMELEMENT pElement; hPDU = L_DicomGetAssociate(hNet); if (!hPDU) { // The connection is not associated, so ignore the message return; } hDlg = (HWND) pUserData; EnableClientTimeout(hDlg, hNet, FALSE); // We have received a C-MOVE-RQ lstrcpy(szBuffer, "C-MOVE-REQUEST received"); if (lstrlen(gszLastLoggedDSFileName)) { wsprintf(szBuffer, "%s and logged into: %s", szBuffer, gszLastLoggedDSFileName); } pszUser = L_DicomGetCalling(hPDU); LogEvent(hDlg, pszUser, szBuffer); LastClientAction(hDlg, hNet, "C-MOVE-REQUEST", TRUE); nStatus = COMMAND_STATUS_SUCCESS; // Are we still processing a previous C-MOVE-RQ? if (L_DicomIsConnected(ghClient)) { nStatus = COMMAND_STATUS_REFUSED_UNABLE_PERFORM_SUB_OPS; } else { pUID = L_DicomFindUID(pszClass); if (!pUID) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else { nID = L_DicomFindAbstract(hPDU, pszClass); if (nID == 0 || L_DicomGetResult(hPDU, nID) != PDU_ACCEPT_RESULT_SUCCESS) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; wsprintf(szBuffer, "C-MOVE-REQUEST - Abstract Syntax, %s, Not Supported by Association!", pUID->pszName); LogEvent(hDlg, pszUser, szBuffer); } } } hUserListCtrl = GetDlgItem(hDlg, IDC_LIST1); if (nStatus == COMMAND_STATUS_SUCCESS) { // Do we know the Move Destination? iClientsCount = ListView_GetItemCount(hUserListCtrl); for (iClientIndex = 0; iClientIndex < iClientsCount; iClientIndex++) { ListView_GetItemText(hUserListCtrl, iClientIndex, 0, szBuffer, sizeof(szBuffer) / sizeof(szBuffer[0])); if (lstrcmpi(pszMoveAE, szBuffer) == 0) { ListView_GetItemText(hUserListCtrl, iClientIndex, 1, szBuffer, sizeof(szBuffer) / sizeof(szBuffer[0])); lstrcpyn(szMoveDstIP, szBuffer, sizeof(szMoveDstIP) / sizeof(szMoveDstIP[0])); ListView_GetItemText(hUserListCtrl, iClientIndex, 2, szBuffer, sizeof(szBuffer) / sizeof(szBuffer[0])); uMoveDstPort = atoi(szBuffer); break; } } if (iClientIndex < iClientsCount) { if (hDS) { // Get the Query/Retrieve Level pElement = L_DicomFindFirstElement(hDS, NULL, TAG_QUERY_RETRIEVE_LEVEL, FALSE); if (pElement) { pszQRLevel = L_DicomGetStringValue(hDS, pElement, 0, 1); ghMoveNet = hNet; lstrcpyn(gszMoveDstAE, pszMoveAE, sizeof(gszMoveDstAE) / sizeof(gszMoveDstAE[0])); gnMovePresentationID = nPresentationID; guMoveMessageID = nMessageID; lstrcpyn(gszMoveClass, pszClass, sizeof(gszMoveClass) / sizeof(gszMoveClass[0])); if (lstrcmp(pszClass, UID_PATIENT_ROOT_QUERY_MOVE) == 0) { if (lstrcmpi(pszQRLevel, "PATIENT") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 1, TRUE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "STUDY") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 2, TRUE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "SERIES") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 3, TRUE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "IMAGE") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 4, TRUE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else { nStatus = lstrlen(pszQRLevel) ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else if (lstrcmp(pszClass, UID_STUDY_ROOT_QUERY_MOVE) == 0) { if (lstrcmpi(pszQRLevel, "STUDY") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 2, FALSE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "SERIES") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 3, FALSE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else if (lstrcmpi(pszQRLevel, "IMAGE") == 0) { MoveImages(hDlg, hDS, szMoveDstIP, uMoveDstPort, 4, FALSE, pszUser); EnableClientTimeout(hDlg, hNet, TRUE); return; } else { nStatus = lstrlen(pszQRLevel) ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } else { nStatus = COMMAND_STATUS_MISSING_ATTRIBUTE; } } else { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } else { nStatus = COMMAND_STATUS_REFUSED_MOVE_DESTINATION_UNKNOWN; } } L_DicomSendCMoveResponse(hNet, nPresentationID, nMessageID, pszClass, nStatus, 0, 0, 0, 0, NULL); LogEvent(hDlg, pszUser, "C-MOVE-RESPONSE sent"); EnableClientTimeout(hDlg, hNet, TRUE); } L_VOID L_EXPORT EXT_CALLBACK ConnectCallback(HDICOMNET hNet, L_INT nError, L_VOID* pUserData) { HWND hDlg; L_UCHAR nPresContID; LPSTR pszAbstractSynName, pszTransSynName; int i, j, iPresContsCount, iTransSynsCount; if (!gpMoveMatches) return; hDlg = (HWND) pUserData; // Failed to connect to the Move Destination AE? if (nError != DICOM_SUCCESS) { free(gpMoveMatches); gpMoveMatches = NULL; gnMatchesCount = 0; L_DicomSendCMoveResponse(ghMoveNet, gnMovePresentationID, guMoveMessageID, gszMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); LogEvent(hDlg, L_DicomGetCalling(L_DicomGetAssociate(ghMoveNet)), "C-MOVE-RESPONSE sent"); return; } /* Prepare the Association request */ // The Protocol Version L_DicomSetVersion(ghMovePDU, PROTOCOL_VERSION); // Titles of the Called and Calling AEs L_DicomSetCalling(ghMovePDU, gszServerAETitle); L_DicomSetCalled(ghMovePDU, gszMoveDstAE); // The Application Context Name L_DicomSetApplication(ghMovePDU, UID_APPLICATION_CONTEXT_NAME); // Presentation Contexts nPresContID = 1; // Add a Presentation Context for the Verification Abstract Syntax L_DicomAddPresentation(ghMovePDU, nPresContID, 0, UID_VERIFICATION_CLASS); L_DicomAddTransfer(ghMovePDU, nPresContID, UID_IMPLICIT_VR_LITTLE_ENDIAN); for (i = 0; i < gnMatchesCount; i++) { pszAbstractSynName = gpMoveMatches[i].szSOPClassUID; // Add a Presentation Context for this Abstract Syntax if we haven't already done this iPresContsCount = L_DicomGetPresentationCount(ghMovePDU); for (j = 0; j < iPresContsCount; j++) { nPresContID = L_DicomGetPresentation(ghMovePDU, j); if (lstrcmp(pszAbstractSynName, L_DicomGetAbstract(ghMovePDU, nPresContID)) == 0) { break; } } if (j >= iPresContsCount) // A new Abstract Syntax? { nPresContID += 2; L_DicomAddPresentation(ghMovePDU, nPresContID, 0, pszAbstractSynName); } pszTransSynName = gpMoveMatches[i].szTransferSyntaxUID; // Add the Transfer Syntax sTransSynName for the Abstract Syntax if we haven't already // done this iTransSynsCount = L_DicomGetTransferCount(ghMovePDU, nPresContID); for (j = 0; j < iTransSynsCount; j++) { if (lstrcmp(pszTransSynName, L_DicomGetTransfer(ghMovePDU, nPresContID, j)) == 0) { break; } } if (j >= iTransSynsCount) // A new Transfer Syntax? { L_DicomAddTransfer(ghMovePDU, nPresContID, pszTransSynName); } } // Ensure that the default Transfer Syntax is added to all Presentation Contexts. Also, set // the role. iPresContsCount = L_DicomGetPresentationCount(ghMovePDU); for (i = 0; i < iPresContsCount; i++) { nPresContID = L_DicomGetPresentation(ghMovePDU, i); iTransSynsCount = L_DicomGetTransferCount(ghMovePDU, nPresContID); for (j = 0; j < iTransSynsCount; j++) { if (lstrcmp(L_DicomGetTransfer(ghMovePDU, nPresContID, j), UID_IMPLICIT_VR_LITTLE_ENDIAN) == 0) { break; } } if (j >= iTransSynsCount) { L_DicomAddTransfer(ghMovePDU, nPresContID, UID_IMPLICIT_VR_LITTLE_ENDIAN); } // Role Selection L_DicomSetRoleSelect(ghMovePDU, nPresContID, TRUE, PDU_ROLE_SUPPORT, PDU_ROLE_SUPPORT); } // Maximum Length Application PDU L_DicomSetMaxLength(ghMovePDU, TRUE, 0x100000); // Implementation Class UID L_DicomSetImplementClass(ghMovePDU, TRUE, IMPLEMENTATION_CLASS_UID); // Implementation Version Name L_DicomSetImplementVersion(ghMovePDU, TRUE, IMPLEMENTATION_VERSION_NAME); // Now send the Association request gnSecondsElapsed = 0; SetTimer(hDlg, MOVE_TIMER_ID, 1000, NULL); L_DicomSendAssociateRequest(hNet, ghMovePDU); LogEvent(hDlg, gszServerAETitle, "Associate Request sent"); } L_CHAR *GetTLSErrorString(L_INT nError) { char *pszError = ""; switch (nError) { case DICOM_ERROR_FORMAT: pszError = "Error Format"; break; case DICOM_ERROR_TLS_CLOSE_NOTIFY: pszError = "Error TLS Close Notify"; break; case DICOM_ERROR_TLS_UNEXPECTED_MESSAGE: pszError = "Error TLS Unexpected Message"; break; case DICOM_ERROR_TLS_BAD_RECORD_MAC: pszError = "Error TLS Bad Record MAC"; break; case DICOM_ERROR_TLS_DECRYPT_FAILED: pszError = "Error TLS Decrypt Failed"; break; case DICOM_ERROR_TLS_RECORD_OVERFLOW: pszError = "Error TLS Record Overflow"; break; case DICOM_ERROR_TLS_DECOMPRESSION_FAILURE: pszError = "Error TLS Decompression Failure"; break; case DICOM_ERROR_TLS_HANDSHAKE_FAILURE: pszError = "Error TLS Handshake Failure"; break; case DICOM_ERROR_TLS_BAD_CERTIFICATE: pszError = "Error TLS Bad Certificate"; break; case DICOM_ERROR_TLS_UNSUPPORTED_CERTIFICATE: pszError = "Error TLS Unsupported Certificate"; break; case DICOM_ERROR_TLS_CERTIFICATE_REVOKED: pszError = "Error TLS Certificate Revoked"; break; case DICOM_ERROR_TLS_CERTIFICATE_EXPIRED: pszError = "Error TLS Certificate Expired"; break; case DICOM_ERROR_TLS_CERTIFICATE_UNKNOWN: pszError = "Error TLS Certificate Unknown"; break; case DICOM_ERROR_TLS_ILLEGAL_PARAMETER: pszError = "Error TLS Illegal Parameter"; break; case DICOM_ERROR_TLS_UNKNOWN_CA: pszError = "Error TLS Unknown CA"; break; case DICOM_ERROR_TLS_ACCESS_DENIED: pszError = "Error TLS Access Denied"; break; case DICOM_ERROR_TLS_DECODE_ERROR: pszError = "Error TLS Decode Error"; break; case DICOM_ERROR_TLS_DECRYPT_ERROR: pszError = "Error TLS Decrypt Error"; break; case DICOM_ERROR_TLS_EXPORT_RESTRICTION: pszError = "Error TLS Export Restriction"; break; case DICOM_ERROR_TLS_PROTOCOL_VERSION: pszError = "Error TLS Protocol Version"; break; case DICOM_ERROR_TLS_INSUFFICIENT_SECURITY: pszError = "Error TLS Insufficient Security"; break; case DICOM_ERROR_TLS_INTERNAL_ERROR: pszError = "Error TLS Internal Error"; break; case DICOM_ERROR_TLS_USER_CANCELED: pszError = "Error TLS User Canceled"; break; case DICOM_ERROR_TLS_NO_RENEGOTIATION: pszError = "Error TLS No Renegotiation"; break; case DICOM_ERROR_TLS_NO_KEEPALIVE: pszError = "Error TLS No Keepalive"; break; case DICOM_ERROR_TLS_CLOSED_CONTROLLED: pszError = "Error TLS Closed Controlled"; break; case DICOM_ERROR_TLS_UNABLE_TO_GET_ISSUER_CERT: pszError = "Error TLS UNABLE TO GET ISSUER_CERT"; break; case DICOM_ERROR_TLS_UNABLE_TO_GET_CRL: pszError = "Error TLS UNABLE TO GET CRL"; break; case DICOM_ERROR_TLS_UNABLE_TO_DECRYPT_CERT_SIGNATURE: pszError = "Error TLS UNABLE TO DECRYPT CERT SIGNATURE"; break; case DICOM_ERROR_TLS_UNABLE_TO_DECRYPT_CRL_SIGNATURE: pszError = "Error TLS UNABLE TO DECRYPT CRL SIGNATURE"; break; case DICOM_ERROR_TLS_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: pszError = "Error TLS UNABLE TO DECODE ISSUER PUBLIC KEY"; break; case DICOM_ERROR_TLS_CERT_SIGNATURE_FAILURE: pszError = "Error TLS Cert Signature Failure"; break; case DICOM_ERROR_TLS_CRL_SIGNATURE_FAILURE: pszError = "Error TLS Crl Signature Failure"; break; case DICOM_ERROR_TLS_CERT_NOT_YET_VALID: pszError = "Error TLS CERT NOT YET VALID"; break; case DICOM_ERROR_TLS_CERT_HAS_EXPIRED: pszError = "Error TLS Cert Has Expired"; break; case DICOM_ERROR_TLS_CRL_NOT_YET_VALID: pszError = "Error TLS CRL Not Yet Valid"; break; case DICOM_ERROR_TLS_CRL_HAS_EXPIRED: pszError = "Error TLS CRL Has Expired"; break; case DICOM_ERROR_TLS_ERROR_IN_CERT_NOT_BEFORE_FIELD: pszError = "Error TLS Error In Cert Not Before Field"; break; case DICOM_ERROR_TLS_ERROR_IN_CERT_NOT_AFTER_FIELD: pszError = "Error TLS Error In Cert Not After Field"; break; case DICOM_ERROR_TLS_ERROR_IN_CRL_LAST_UPDATE_FIELD: pszError = "Error TLS Error In Crl Last Update Field"; break; case DICOM_ERROR_TLS_ERROR_IN_CRL_NEXT_UPDATE_FIELD: pszError = "Error TLS Error In Crl Next Update Field"; break; case DICOM_ERROR_TLS_OUT_OF_MEM: pszError = "Error TLS Out Of Memory"; break; case DICOM_ERROR_TLS_DEPTH_ZERO_SELF_SIGNED_CERT: pszError = "Error TLS Depth Zero Self Signed Cert"; break; case DICOM_ERROR_TLS_SELF_SIGNED_CERT_IN_CHAIN: pszError = "Error TLS Self Signed Cert In Chain"; break; case DICOM_ERROR_TLS_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: pszError = "Error TLS Unable To Get Issuer Cert Locally"; break; case DICOM_ERROR_TLS_UNABLE_TO_VERIFY_LEAF_SIGNATURE: pszError = "Error TLS Unable To Verify Leaf Signature"; break; case DICOM_ERROR_TLS_CERT_CHAIN_TOO_LONG: pszError = "Error TLS Cert Chain Too Long"; break; case DICOM_ERROR_TLS_CERT_REVOKED: pszError = "Error TLS Cert Revoked"; break; case DICOM_ERROR_TLS_INVALID_CA: pszError = "Error TLS Invalid CA"; break; case DICOM_ERROR_TLS_PATH_LENGTH_EXCEEDED: pszError = "Error TLS Path Length Exceeded"; break; case DICOM_ERROR_TLS_INVALID_PURPOSE: pszError = "Error TLS Invalid Purpose"; break; case DICOM_ERROR_TLS_CERT_UNTRUSTED: pszError = "Error TLS Cert Untrusted"; break; case DICOM_ERROR_TLS_CERT_REJECTED: pszError = "Error TLS Cert Rejected"; break; case DICOM_ERROR_TLS_SUBJECT_ISSUER_MISMATCH: pszError = "Error TLS Subject Issuer Mismatch"; break; case DICOM_ERROR_TLS_AKID_SKID_MISMATCH: pszError = "Error TLS Akid Skid Mismatch"; break; case DICOM_ERROR_TLS_AKID_ISSUER_SERIAL_MISMATCH: pszError = "Error TLS Akid Issuer Serial Mismatch"; break; case DICOM_ERROR_TLS_KEYUSAGE_NO_CERTSIGN: pszError = "Error TLS Keyusage No Certsign"; break; case DICOM_ERROR_TLS_APPLICATION_VERIFICATION: pszError = "Error TLS Application Verification"; break; case DICOM_ERROR_TLS_INVALID_CTX: pszError = "Error TLS Invalid CTX"; break; case DICOM_ERROR_TLS_INVALID_CTX_VERIFY_DEPTH: pszError = "Error TLS Invalid CTX Verify Depth"; break; case DICOM_ERROR_TLS_INVALID_CTX_VERIFY_MODE: pszError = "Error TLS Invalid CTX Verify Mode"; break; case DICOM_ERROR_TLS_INVALID_CTX_CAFILE: pszError = "Error TLS Invalid CTX Cafile"; break; case DICOM_ERROR_TLS_INVALID_CTX_OPTIONS: pszError = "Error TLS Invalid CTX Options"; break; } return pszError; } L_VOID L_EXPORT EXT_CALLBACK ReceiveCallback(HDICOMNET hNet, L_INT nError, L_UCHAR nType, L_CHAR *pBuffer, L_UINT32 nBytes, L_VOID *pUserData) { L_CHAR *pszError = ""; L_CHAR szMsg[200]; HWND hDlg = (HWND) pUserData; switch(nError) { case DICOM_SUCCESS: // do nothing break; case DICOM_ERROR_FORMAT: case DICOM_ERROR_TLS_CLOSE_NOTIFY: case DICOM_ERROR_TLS_UNEXPECTED_MESSAGE: case DICOM_ERROR_TLS_BAD_RECORD_MAC: case DICOM_ERROR_TLS_DECRYPT_FAILED: case DICOM_ERROR_TLS_RECORD_OVERFLOW: case DICOM_ERROR_TLS_DECOMPRESSION_FAILURE: case DICOM_ERROR_TLS_HANDSHAKE_FAILURE: case DICOM_ERROR_TLS_BAD_CERTIFICATE: case DICOM_ERROR_TLS_UNSUPPORTED_CERTIFICATE: case DICOM_ERROR_TLS_CERTIFICATE_REVOKED: case DICOM_ERROR_TLS_CERTIFICATE_EXPIRED: case DICOM_ERROR_TLS_CERTIFICATE_UNKNOWN: case DICOM_ERROR_TLS_ILLEGAL_PARAMETER: case DICOM_ERROR_TLS_UNKNOWN_CA: case DICOM_ERROR_TLS_ACCESS_DENIED: case DICOM_ERROR_TLS_DECODE_ERROR: case DICOM_ERROR_TLS_DECRYPT_ERROR: case DICOM_ERROR_TLS_EXPORT_RESTRICTION: case DICOM_ERROR_TLS_PROTOCOL_VERSION: case DICOM_ERROR_TLS_INSUFFICIENT_SECURITY: case DICOM_ERROR_TLS_INTERNAL_ERROR: case DICOM_ERROR_TLS_USER_CANCELED: case DICOM_ERROR_TLS_NO_RENEGOTIATION: case DICOM_ERROR_TLS_NO_KEEPALIVE: case DICOM_ERROR_TLS_CLOSED_CONTROLLED: pszError = GetTLSErrorString(nError); // close the connection wsprintf(szMsg, "[%d]%s -- closing connection", nError, pszError); LogEvent(hDlg, "", szMsg); L_DicomClose(hNet); break; default: // Just log a message wsprintf(szMsg, "Error[%d]", nError); LogEvent(hDlg, "", szMsg); break; } } L_VOID L_EXPORT EXT_CALLBACK ReceiveDataCallback(HDICOMNET hNet, L_UCHAR nPresentationID, HDICOMDS hCS, HDICOMDS hDS, L_VOID *pUserData) { SaveSet(hCS, FALSE, TRUE, NULL); SaveSet(hDS, TRUE, TRUE, gszLastLoggedDSFileName); } L_VOID LoadDefaultSettings() { // ISCL Auth gISCLAuthAlg = DICOM_ISCL_MUTUAL_AUTH_3P4W; gISCLAuthKeys[0] = 11619789628100321; gISCLAuthKeys[1] = 34217865672122111; gISCLAuthKeys[2] = 1605935625518899689; gISCLAuthKeys[3] = 138217077775855676; gISCLAuthKeys[4] = 9117318694593010212; gISCLAuthKeys[5] = 3485297985488245687; gISCLAuthKeys[6] = 1533287511573403981; gISCLAuthKeys[7] = 5604839976916070822; gISCLAuthCurrentKey = 1; //ISCL Encrypt gISCLEncAlg = DICOM_ISCL_ENCRYPT_DESCBC; gISCLEncSign = DICOM_ISCL_MAC_MD5; gISCLEncKeys[0] = 8079278526052745737; gISCLEncKeys[1] = 1312864321990916052; gISCLEncKeys[2] = 7190959962252002117; gISCLEncKeys[3] = 3619524191167482890; gISCLEncKeys[4] = 3466658849848898336; gISCLEncKeys[5] = 8474124475946342520; gISCLEncKeys[6] = 7725464453540259890; gISCLEncKeys[7] = 4320705344832296668; gISCLEncCurrentKey = 1; } L_BOOL LoadFromFileSettings() { FILE *file; L_CHAR szbuffer[100]; L_INT i; file = fopen("secur.ini", "r"); if (file != NULL) { // ISCL Auth Settings if (fgets(szbuffer, 100, file) != NULL) { gISCLAuthAlg = (L_UINT32) _atoi64(szbuffer); } else { return FALSE; } for (i = 0; i < 8; i++) { if (fgets(szbuffer, 100, file) != NULL) { gISCLAuthKeys[i] = _atoi64(szbuffer); } else { return FALSE; } } if (fgets(szbuffer, 100, file) != NULL) { gISCLAuthCurrentKey = (L_UINT32) _atoi64(szbuffer); } else { return FALSE; } // ISCL Encr Settings if (fgets(szbuffer, 100, file) != NULL) { gISCLEncAlg = (L_UINT32) _atoi64(szbuffer); } else { return FALSE; } if (fgets(szbuffer, 100, file) != NULL) { gISCLEncSign = (L_UINT32) _atoi64(szbuffer); } else { return FALSE; } for (i = 0; i < 8; i++) { if (fgets(szbuffer, 100, file) != NULL) { gISCLEncKeys[i] = _atoi64(szbuffer); } else { return FALSE; } } if (fgets(szbuffer, 100, file) != NULL) { gISCLEncCurrentKey = (L_UINT32) _atoi64(szbuffer); } else { return FALSE; } fclose(file); } else { return FALSE; } return TRUE; } L_BOOL SendMatchedInstance(HWND hDlg, HDICOMNET hNet, LPSTR pszFailedSOPInstanceUID) { char szFile[MAX_PATH], szLoggedFile[MAX_PATH], szMsg[512]; LPSTR pszSOPClassUID, pszTransferSyntaxUID, pszReferencedFile; L_CHAR* pszSOPInstanceUID; HDICOMPDU hPDU; L_UCHAR nPresentationID; HDICOMDS hDataSet; L_UINT16 uRet; pDICOMELEMENT pElement; L_INT nRet; if (pszFailedSOPInstanceUID) { lstrcpy(pszFailedSOPInstanceUID, ""); } gnMatchIndex++; if (gnMatchIndex >= gnMatchesCount) { return FALSE; } // Get the information of the match to be sent pszSOPClassUID = gpMoveMatches[gnMatchIndex].szSOPClassUID; pszTransferSyntaxUID = gpMoveMatches[gnMatchIndex].szTransferSyntaxUID; pszReferencedFile = gpMoveMatches[gnMatchIndex].szReferencedFile; hPDU = L_DicomGetAssociate(ghClient); if(hPDU == NULL) { return FALSE; } nPresentationID = L_DicomFindAbstract(hPDU, pszSOPClassUID); if (nPresentationID == 0 || L_DicomGetResult(hPDU, nPresentationID) != PDU_ACCEPT_RESULT_SUCCESS) { return FALSE; } hDataSet = L_DicomCreateDS(gszTempFilesFolder); wsprintf(szFile, "%s%s", gszImagesFolder, pszReferencedFile); uRet = L_DicomLoadDS(hDataSet, szFile, DS_LOAD_CLOSE); if (uRet != DICOM_SUCCESS) { L_DicomFreeDS(hDataSet); return FALSE; } ChangeTransferSyntax(hDataSet, pszTransferSyntaxUID, L_DicomGetTransfer(hPDU, nPresentationID, 0)); pszSOPInstanceUID = ""; pElement = L_DicomFindFirstElement(hDataSet, NULL, TAG_SOP_INSTANCE_UID, FALSE); if (pElement) { pszSOPInstanceUID = L_DicomGetStringValue(hDataSet, pElement, 0, 1); } // Now send the instance gnSecondsElapsed = 0; SetTimer(hDlg, MOVE_TIMER_ID, 1000, NULL); hPDU = L_DicomGetAssociate(ghMoveNet); nRet = L_DicomSendCStoreRequest(hNet, nPresentationID, guStoreMessageID++, pszSOPClassUID, pszSOPInstanceUID, COMMAND_PRIORITY_MEDIUM, L_DicomGetCalling(hPDU), guMoveMessageID, hDataSet); if (nRet == DICOM_SUCCESS) { SaveSet(hDataSet, TRUE, FALSE, szLoggedFile); lstrcpy(szMsg, "C-STORE-REQUEST sent"); if (lstrlen(szLoggedFile)) { wsprintf(szMsg, "%s and logged into: %s", szMsg, szLoggedFile); } LogEvent(hDlg, gszServerAETitle, szMsg); L_DicomFreeDS(hDataSet); return TRUE; } if (pszFailedSOPInstanceUID) { lstrcpy(pszFailedSOPInstanceUID, pszSOPInstanceUID); } LogEvent(hDlg, gszServerAETitle, "C-STORE-REQUEST sent"); L_DicomFreeDS(hDataSet); return FALSE; } L_BOOL ChangeTransferSyntax(HDICOMDS hDataSet, LPCSTR pszDataSetTS, LPCSTR pszAssociatedTS) { pDICOMELEMENT pElement; // No need to change the Transfer Syntax if it is the same as the associated one. if (lstrcmp(pszDataSetTS, pszAssociatedTS) == 0) { return TRUE; } // Also, no need to change the Transfer Syntax explicitly if both belong to this group // of Transfer Syntaxes. if (lstrcmp(pszAssociatedTS, UID_IMPLICIT_VR_LITTLE_ENDIAN) == 0 || lstrcmp(pszAssociatedTS, UID_EXPLICIT_VR_LITTLE_ENDIAN) == 0 || lstrcmp(pszAssociatedTS, UID_EXPLICIT_VR_BIG_ENDIAN) == 0) { if (lstrcmp(pszDataSetTS, UID_IMPLICIT_VR_LITTLE_ENDIAN) == 0 || lstrcmp(pszDataSetTS, UID_EXPLICIT_VR_LITTLE_ENDIAN) == 0 || lstrcmp(pszDataSetTS, UID_EXPLICIT_VR_BIG_ENDIAN) == 0) { return TRUE; } } /* We need to change the Transfer Syntax of the Data Set */ if (L_DicomChangeTransferSyntax(hDataSet, (L_CHAR*) pszAssociatedTS, 2, 0) != DICOM_SUCCESS) { return FALSE; } // Has the image undergone a lossy compression? if (lstrcmp(pszDataSetTS, UID_JPEG_BASELINE_1) == 0 || lstrcmp(pszDataSetTS, UID_JPEG_EXTENDED_2_4) == 0 || lstrcmp(pszDataSetTS, UID_JPEG2000) == 0) { pElement = L_DicomFindFirstElement(hDataSet, NULL, TAG_LOSSY_IMAGE_COMPRESSION, FALSE); if (pElement == NULL) { pElement = L_DicomInsertElement(hDataSet, NULL, FALSE, TAG_LOSSY_IMAGE_COMPRESSION, VR_CS, FALSE, 0); } if (pElement) { L_DicomSetStringValue(hDataSet, pElement, "01", 1); } } return TRUE; } L_UINT32 L_EXPORT EXT_CALLBACK exGetChallengeISCL(HDICOMNET hNet, L_UINT64 *nChallenge, L_UINT64 nParameter, L_VOID *pUserData) { *nChallenge = 0x0123456789ABCDE1; return 0; }; L_UINT32 L_EXPORT EXT_CALLBACK exInternalAuthenticateISCL(HDICOMNET hNet, L_UINT64 nChallenge, L_UINT64 *nResponse, L_UINT64 nParameter, L_VOID *pUserData) { *nResponse = nChallenge + 1; return 0; }; L_UINT32 L_EXPORT EXT_CALLBACK exExternalAuthenticateISCL(HDICOMNET hNet, L_UINT64 nChallenge, L_UINT64 nResponse, L_UINT64 nParameter, L_VOID *pUserData) { if (nResponse == nChallenge + 1) return 0; else return 1; }; L_VOID L_EXPORT EXT_CALLBACK exOnSecureLinkReady(HDICOMNET hNet, L_UINT32 nError, L_VOID *pUserData) { char str[128]; sprintf(str, "ISCL Link error %d", nError); // MessageBox(NULL, str, "Message", MB_OK); }; L_VOID L_EXPORT EXT_CALLBACK exOnReceivedISCLPacket(HDICOMNET hNet, L_INT nError, L_CHAR *pBuffer, L_UINT32 nBytes, L_VOID *pUserData) { L_UINT32 nMsgId, nOption, nPacketLoadLength; nMsgId = *((L_UINT32 *)&(pBuffer[4])); nPacketLoadLength = *((L_UINT32 *)&(pBuffer[8])); nOption = *((L_UINT32 *)&(pBuffer[12])); };