Mercurial > hg > orthanc
comparison OrthancServer/Sources/main.cpp @ 4899:3ab57510f6dd proto-filter-instance-returning-error-code
integration mainline->proto-filter-instance-returning-error-code
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sun, 20 Feb 2022 12:59:30 +0100 |
parents | 656784ac6759 6eff25f70121 |
children |
comparison
equal
deleted
inserted
replaced
4791:656784ac6759 | 4899:3ab57510f6dd |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
4 * Department, University Hospital of Liege, Belgium | 4 * Department, University Hospital of Liege, Belgium |
5 * Copyright (C) 2017-2021 Osimis S.A., Belgium | 5 * Copyright (C) 2017-2022 Osimis S.A., Belgium |
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | |
6 * | 7 * |
7 * This program is free software: you can redistribute it and/or | 8 * This program is free software: you can redistribute it and/or |
8 * modify it under the terms of the GNU General Public License as | 9 * modify it under the terms of the GNU General Public License as |
9 * published by the Free Software Foundation, either version 3 of the | 10 * published by the Free Software Foundation, either version 3 of the |
10 * License, or (at your option) any later version. | 11 * License, or (at your option) any later version. |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | 12 * |
24 * This program is distributed in the hope that it will be useful, but | 13 * This program is distributed in the hope that it will be useful, but |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | 14 * WITHOUT ANY WARRANTY; without even the implied warranty of |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
27 * General Public License for more details. | 16 * General Public License for more details. |
82 context_(context) | 71 context_(context) |
83 { | 72 { |
84 } | 73 } |
85 | 74 |
86 | 75 |
87 virtual int Handle(DcmDataset& dicom, | 76 virtual uint16_t Handle(DcmDataset& dicom, |
88 const std::string& remoteIp, | 77 const std::string& remoteIp, |
89 const std::string& remoteAet, | 78 const std::string& remoteAet, |
90 const std::string& calledAet) ORTHANC_OVERRIDE | 79 const std::string& calledAet) ORTHANC_OVERRIDE |
91 { | 80 { |
92 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromDcmDataset(dicom)); | 81 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromDcmDataset(dicom)); |
93 | 82 |
94 if (toStore->GetBufferSize() > 0) | 83 if (toStore->GetBufferSize() > 0) |
95 { | 84 { |
96 toStore->SetOrigin(DicomInstanceOrigin::FromDicomProtocol | 85 toStore->SetOrigin(DicomInstanceOrigin::FromDicomProtocol |
97 (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str())); | 86 (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str())); |
98 | 87 |
99 std::string id; | 88 std::string id; |
100 StoreStatus res = context_.Store(id, *toStore, StoreInstanceMode_Default); | 89 ServerContext::StoreResult result = context_.Store(id, *toStore, StoreInstanceMode_Default); |
101 if (res == StoreStatus_FilteredOut) | 90 |
102 { | 91 if (result.GetStatus() == StoreStatus_FilteredOut) |
103 return 0xA700; // C-Store "Out of Resources" error code | 92 { |
104 } | 93 return STATUS_STORE_Refused_OutOfResources; // C-Store "Out of Resources" error code (0xa700) |
105 return 0x0000; | 94 } |
106 } | 95 else |
107 | 96 { |
108 return 0xC000; // C-Store "Cannot understand" error code | 97 return result.GetCStoreStatusCode(); |
98 } | |
99 } | |
100 | |
101 return STATUS_STORE_Error_CannotUnderstand; // C-Store "Cannot understand" error code (0xc000) | |
109 } | 102 } |
110 }; | 103 }; |
111 | 104 |
112 | 105 |
113 | 106 |
283 { | 276 { |
284 private: | 277 private: |
285 ServerContext& context_; | 278 ServerContext& context_; |
286 bool alwaysAllowEcho_; | 279 bool alwaysAllowEcho_; |
287 bool alwaysAllowFind_; // New in Orthanc 1.9.0 | 280 bool alwaysAllowFind_; // New in Orthanc 1.9.0 |
281 bool alwaysAllowFindWorklist_; // New in Orthanc 1.10.0 | |
288 bool alwaysAllowGet_; // New in Orthanc 1.9.0 | 282 bool alwaysAllowGet_; // New in Orthanc 1.9.0 |
289 bool alwaysAllowMove_; // New in Orthanc 1.9.7 | 283 bool alwaysAllowMove_; // New in Orthanc 1.9.7 |
290 bool alwaysAllowStore_; | 284 bool alwaysAllowStore_; |
291 | 285 |
292 public: | 286 public: |
295 { | 289 { |
296 { | 290 { |
297 OrthancConfiguration::ReaderLock lock; | 291 OrthancConfiguration::ReaderLock lock; |
298 alwaysAllowEcho_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowEcho", true); | 292 alwaysAllowEcho_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowEcho", true); |
299 alwaysAllowFind_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowFind", false); | 293 alwaysAllowFind_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowFind", false); |
294 alwaysAllowFindWorklist_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowFindWorklist", false); | |
300 alwaysAllowGet_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowGet", false); | 295 alwaysAllowGet_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowGet", false); |
301 alwaysAllowMove_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowMove", false); | 296 alwaysAllowMove_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowMove", false); |
302 alwaysAllowStore_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowStore", true); | 297 alwaysAllowStore_ = lock.GetConfiguration().GetBooleanParameter("DicomAlwaysAllowStore", true); |
303 } | 298 } |
304 | 299 |
305 if (alwaysAllowFind_) | 300 if (alwaysAllowFind_) |
306 { | 301 { |
307 LOG(WARNING) << "Security risk in DICOM SCP: C-FIND requests are always allowed, even from unknown modalities"; | 302 LOG(WARNING) << "Security risk in DICOM SCP: C-FIND requests are always allowed, even from unknown modalities"; |
308 } | 303 } |
309 | 304 |
305 if (alwaysAllowFindWorklist_) | |
306 { | |
307 LOG(WARNING) << "Security risk in DICOM SCP: C-FIND requests for worklists are always allowed, even from unknown modalities"; | |
308 } | |
309 | |
310 if (alwaysAllowGet_) | 310 if (alwaysAllowGet_) |
311 { | 311 { |
312 LOG(WARNING) << "Security risk in DICOM SCP: C-GET requests are always allowed, even from unknown modalities"; | 312 LOG(WARNING) << "Security risk in DICOM SCP: C-GET requests are always allowed, even from unknown modalities"; |
313 } | 313 } |
314 | 314 |
325 LOG(INFO) << "Incoming connection from AET " << remoteAet | 325 LOG(INFO) << "Incoming connection from AET " << remoteAet |
326 << " on IP " << remoteIp << ", calling AET " << calledAet; | 326 << " on IP " << remoteIp << ", calling AET " << calledAet; |
327 | 327 |
328 if (alwaysAllowEcho_ || | 328 if (alwaysAllowEcho_ || |
329 alwaysAllowFind_ || | 329 alwaysAllowFind_ || |
330 alwaysAllowFindWorklist_ || | |
330 alwaysAllowGet_ || | 331 alwaysAllowGet_ || |
331 alwaysAllowMove_ || | 332 alwaysAllowMove_ || |
332 alwaysAllowStore_) | 333 alwaysAllowStore_) |
333 { | 334 { |
334 return true; | 335 return true; |
342 | 343 |
343 static void ReportDisallowedCommand(const std::string& remoteIp, | 344 static void ReportDisallowedCommand(const std::string& remoteIp, |
344 const std::string& remoteAet, | 345 const std::string& remoteAet, |
345 DicomRequestType type) | 346 DicomRequestType type) |
346 { | 347 { |
347 LOG(WARNING) << "Unable to check DICOM authorization for AET " << remoteAet | 348 LOG(WARNING) << "DICOM authorization rejected for AET " << remoteAet |
348 << " on IP " << remoteIp << ": The DICOM command " | 349 << " on IP " << remoteIp << ": The DICOM command " |
349 << EnumerationToString(type) << " is not allowed for this modality " | 350 << EnumerationToString(type) << " is not allowed for this modality " |
350 << "according to configuration option \"DicomModalities\""; | 351 << "according to configuration option \"DicomModalities\""; |
351 } | 352 } |
352 | 353 |
369 alwaysAllowFind_) | 370 alwaysAllowFind_) |
370 { | 371 { |
371 // Incoming C-Find requests are always accepted, even from unknown AET | 372 // Incoming C-Find requests are always accepted, even from unknown AET |
372 return true; | 373 return true; |
373 } | 374 } |
375 else if (type == DicomRequestType_FindWorklist && | |
376 alwaysAllowFindWorklist_) | |
377 { | |
378 // Incoming C-Find requests for worklists are always accepted, even from unknown AET | |
379 return true; | |
380 } | |
374 else if (type == DicomRequestType_Store && | 381 else if (type == DicomRequestType_Store && |
375 alwaysAllowStore_) | 382 alwaysAllowStore_) |
376 { | 383 { |
377 // Incoming C-Store requests are always accepted, even from unknown AET | 384 // Incoming C-Store requests are always accepted, even from unknown AET |
378 return true; | 385 return true; |
400 checkIp = lock.GetConfiguration().GetBooleanParameter("DicomCheckModalityHost", false); | 407 checkIp = lock.GetConfiguration().GetBooleanParameter("DicomCheckModalityHost", false); |
401 } | 408 } |
402 | 409 |
403 if (modalities.empty()) | 410 if (modalities.empty()) |
404 { | 411 { |
405 LOG(WARNING) << "Unable to check DICOM authorization for AET " << remoteAet | 412 LOG(WARNING) << "DICOM authorization rejected for AET " << remoteAet |
406 << " on IP " << remoteIp << ": This AET is not listed in " | 413 << " on IP " << remoteIp << ": This AET is not listed in " |
407 << "configuration option \"DicomModalities\""; | 414 << "configuration option \"DicomModalities\""; |
408 return false; | 415 return false; |
409 } | 416 } |
410 else if (modalities.size() == 1) | 417 else if (modalities.size() == 1) |
411 { | 418 { |
412 // DicomCheckModalityHost is true: check if the IP match the configured IP | 419 // DicomCheckModalityHost is true: check if the IP match the configured IP |
413 if (checkIp && | 420 if (checkIp && |
414 remoteIp != modalities.front().GetHost()) | 421 remoteIp != modalities.front().GetHost()) |
415 { | 422 { |
416 LOG(WARNING) << "Unable to check DICOM authorization for AET " << remoteAet | 423 LOG(WARNING) << "DICOM authorization rejected for AET " << remoteAet |
417 << " on IP " << remoteIp << ": Its IP address should be " | 424 << " on IP " << remoteIp << ": Its IP address should be " |
418 << modalities.front().GetHost() | 425 << modalities.front().GetHost() |
419 << " according to configuration option \"DicomModalities\""; | 426 << " according to configuration option \"DicomModalities\""; |
420 return false; | 427 return false; |
421 } | 428 } |
447 return false; | 454 return false; |
448 } | 455 } |
449 } | 456 } |
450 } | 457 } |
451 | 458 |
452 LOG(WARNING) << "Unable to check DICOM authorization for AET " << remoteAet | 459 LOG(WARNING) << "DICOM authorization rejected for AET " << remoteAet |
453 << " on IP " << remoteIp << ": " << modalities.size() | 460 << " on IP " << remoteIp << ": " << modalities.size() |
454 << " modalites found with this AET in configuration option " | 461 << " modalites found with this AET in configuration option " |
455 << "\"DicomModalities\", but none of them matches the IP"; | 462 << "\"DicomModalities\", but none of them matches the IP"; |
456 return false; | 463 return false; |
457 } | 464 } |
716 static void PrintVersion(const char* path) | 723 static void PrintVersion(const char* path) |
717 { | 724 { |
718 std::cout | 725 std::cout |
719 << path << " " << ORTHANC_VERSION << std::endl | 726 << path << " " << ORTHANC_VERSION << std::endl |
720 << "Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics Department, University Hospital of Liege (Belgium)" << std::endl | 727 << "Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics Department, University Hospital of Liege (Belgium)" << std::endl |
721 << "Copyright (C) 2017-2021 Osimis S.A. (Belgium)" << std::endl | 728 << "Copyright (C) 2017-2022 Osimis S.A. (Belgium)" << std::endl |
722 << "Licensing GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>, with OpenSSL exception." << std::endl | 729 << "Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain (Belgium)" << std::endl |
730 << "Licensing GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>." << std::endl | |
723 << "This is free software: you are free to change and redistribute it." << std::endl | 731 << "This is free software: you are free to change and redistribute it." << std::endl |
724 << "There is NO WARRANTY, to the extent permitted by law." << std::endl | 732 << "There is NO WARRANTY, to the extent permitted by law." << std::endl |
725 << std::endl | 733 << std::endl |
726 << "Written by Sebastien Jodogne <s.jodogne@orthanc-labs.com>" << std::endl; | 734 << "Written by Sebastien Jodogne <s.jodogne@orthanc-labs.com>" << std::endl; |
727 } | 735 } |
1215 { | 1223 { |
1216 OrthancConfiguration::ReaderLock lock; | 1224 OrthancConfiguration::ReaderLock lock; |
1217 dicomServer.SetCalledApplicationEntityTitleCheck(lock.GetConfiguration().GetBooleanParameter("DicomCheckCalledAet", false)); | 1225 dicomServer.SetCalledApplicationEntityTitleCheck(lock.GetConfiguration().GetBooleanParameter("DicomCheckCalledAet", false)); |
1218 dicomServer.SetAssociationTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScpTimeout", 30)); | 1226 dicomServer.SetAssociationTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomScpTimeout", 30)); |
1219 dicomServer.SetPortNumber(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomPort", 4242)); | 1227 dicomServer.SetPortNumber(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomPort", 4242)); |
1228 dicomServer.SetThreadsCount(lock.GetConfiguration().GetUnsignedIntegerParameter("DicomThreadsCount", 4)); | |
1220 dicomServer.SetApplicationEntityTitle(lock.GetConfiguration().GetOrthancAET()); | 1229 dicomServer.SetApplicationEntityTitle(lock.GetConfiguration().GetOrthancAET()); |
1221 | 1230 |
1222 // Configuration of DICOM TLS for Orthanc SCP (since Orthanc 1.9.0) | 1231 // Configuration of DICOM TLS for Orthanc SCP (since Orthanc 1.9.0) |
1223 dicomServer.SetDicomTlsEnabled(lock.GetConfiguration().GetBooleanParameter(KEY_DICOM_TLS_ENABLED, false)); | 1232 dicomServer.SetDicomTlsEnabled(lock.GetConfiguration().GetBooleanParameter(KEY_DICOM_TLS_ENABLED, false)); |
1224 if (dicomServer.IsDicomTlsEnabled()) | 1233 if (dicomServer.IsDicomTlsEnabled()) |
1525 context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024); | 1534 context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024); |
1526 } | 1535 } |
1527 catch (...) | 1536 catch (...) |
1528 { | 1537 { |
1529 context.GetIndex().SetMaximumStorageSize(0); | 1538 context.GetIndex().SetMaximumStorageSize(0); |
1539 } | |
1540 | |
1541 try | |
1542 { | |
1543 uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageCacheSize", 128); | |
1544 context.SetMaximumStorageCacheSize(size * 1024 * 1024); | |
1545 } | |
1546 catch (...) | |
1547 { | |
1548 context.SetMaximumStorageCacheSize(128); | |
1530 } | 1549 } |
1531 } | 1550 } |
1532 | 1551 |
1533 { | 1552 { |
1534 ServerContextConfigurator configurator(context, plugins); // This calls "OrthancConfiguration::SetServerIndex()" | 1553 ServerContextConfigurator configurator(context, plugins); // This calls "OrthancConfiguration::SetServerIndex()" |