Skip to content

Conversation

@tmart234
Copy link

Summary

DICOM communications is used in medical imaging systems. This PR adds Scapy layers for the DICOM Upper Layer Protocol (DICOM PS3.8).

Features

  • Full PDU support: A-ASSOCIATE-RQ/AC/RJ, P-DATA-TF, A-RELEASE-RQ/RP, A-ABORT
  • Variable item negotiation: Application Context, Presentation Context, User Information, User Identity, Async Operations, Role Selection
  • DIMSE command packets: C-ECHO, C-STORE, C-FIND, C-MOVE, C-GET

Usage

from scapy.contrib.dicom import *

# Build an A-ASSOCIATE-RQ
pkt = DICOM() / A_ASSOCIATE_RQ(
    called_ae_title=_pad_ae_title("SERVER"),
    calling_ae_title=_pad_ae_title("CLIENT"),
    variable_items=[
        DICOMVariableItem() / DICOMApplicationContext(),
        build_presentation_context_rq(1, VERIFICATION_SOP_CLASS_UID, [DEFAULT_TRANSFER_SYNTAX_UID]),
        build_user_information(max_pdu_length=16384),
    ]
)

# Build a C-MOVE-RQ
move_rq = C_MOVE_RQ(message_id=1, move_destination=b"DEST_AE")

Testing

  • 59 unit tests in test/contrib/dicom.uts
  • All tests pass on Python 3.9+

References

@codecov
Copy link

codecov bot commented Dec 30, 2025

Codecov Report

❌ Patch coverage is 52.64664% with 331 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.63%. Comparing base (a5bc2bb) to head (29d2131).

Files with missing lines Patch % Lines
scapy/contrib/dicom.py 52.64% 331 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4891      +/-   ##
==========================================
- Coverage   80.84%   80.63%   -0.22%     
==========================================
  Files         369      370       +1     
  Lines       90963    91662     +699     
==========================================
+ Hits        73539    73908     +369     
- Misses      17424    17754     +330     
Files with missing lines Coverage Δ
scapy/contrib/dicom.py 52.64% <52.64%> (ø)

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@polydroi
Copy link

Thanks for the PR. I’ve stared the unit tests. Looks good, however, could you please add type hints to your layer?

@tmart234
Copy link
Author

Thanks for the PR. I’ve stared the unit tests. Looks good, however, could you please add type hints to your layer?

Added

@polybassa
Copy link
Contributor

Besides the small Flake8 issues, LGTM.

@gpotter2 do you want to have a final look?

polybassa
polybassa previously approved these changes Jan 9, 2026
@tmart234
Copy link
Author

tmart234 commented Jan 9, 2026

@polybassa fixed the Flake8 issues

@polybassa polybassa requested a review from gpotter2 January 9, 2026 19:24
@gpotter2
Copy link
Member

I trust you @polybassa !

@gpotter2 gpotter2 requested review from Copilot and removed request for gpotter2 January 11, 2026 12:35
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive support for the DICOM (Digital Imaging and Communications in Medicine) Upper Layer Protocol to Scapy, enabling network analysis and interaction with medical imaging systems. The implementation follows the DICOM PS3.8 specification and includes extensive packet definitions for protocol negotiation, data transfer, and DIMSE commands.

Changes:

  • Added complete DICOM protocol layer implementation with PDU types, variable items, and DIMSE command packets
  • Implemented a high-level DICOMSocket class for association management and DIMSE operations
  • Added 59 comprehensive unit tests covering packet construction, parsing, and round-trip serialization

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
scapy/contrib/dicom.py Main implementation file containing DICOM packet classes, field types, PDU definitions, DIMSE commands, and DICOMSocket for application-level operations
test/contrib/dicom.uts Comprehensive test suite with 59 tests covering module loading, PDU construction/parsing, variable items, DIMSE packets, helper functions, and edge cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

value_len = struct.unpack(
"<I", dimse_bytes[offset + 4:offset + 8]
)[0]
if tag_group == 0x0000 and tag_elem == 0x0900 and value_len == 2:
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the parse_dimse_status function, after checking that offset + 8 is within bounds, the code reads the value at offset + 8 to offset + 10 without verifying that offset + 10 is also within bounds. If value_len is 2 but there are fewer than 2 bytes remaining after offset + 8, this could cause an IndexError or read beyond the buffer.

Suggested change
if tag_group == 0x0000 and tag_elem == 0x0900 and value_len == 2:
if (
tag_group == 0x0000
and tag_elem == 0x0900
and value_len == 2
):
# Ensure there are at least 2 bytes available for the status
if offset + 10 > len(dimse_bytes) or offset + 10 > group_end_offset:
break

Copilot uses AI. Check for mistakes.
def getfield(self, pkt: Optional[Packet], s: bytes) -> Tuple[bytes, bytes]:
if len(s) < 8:
return s, b""
tag_g, tag_e, length = struct.unpack("<HHI", s[:8])
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getfield method in DICOMElementField reads a length value from the packet but doesn't validate that sufficient bytes are available before slicing s[8:8 + length]. If length is very large or the packet is truncated, this could read beyond the available data without raising an error, potentially causing silent data corruption or unexpected behavior.

Suggested change
tag_g, tag_e, length = struct.unpack("<HHI", s[:8])
tag_g, tag_e, length = struct.unpack("<HHI", s[:8])
# Ensure the buffer contains the full value as declared by the length
if len(s) < 8 + length:
raise Scapy_Exception(
"Not enough bytes to decode DICOM element value: "
f"expected {length} bytes, only {len(s) - 8} available"
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants