Library dicom

DICOM protocol library for Nmap NSE scripts.

Implements the DICOM Upper Layer protocol (PS3.8) and core DIMSE services (PS3.7). Provides connection management, A-ASSOCIATE negotiation, P-DATA-TF PDU construction/parsing, and helpers for C-ECHO, C-STORE, C-FIND, C-GET, and C-MOVE.

Supports both Implicit VR Little Endian and Explicit VR Little Endian transfer syntaxes for dataset encoding and parsing.

Designed to be shared across multiple DICOM NSE scripts (dicom-ping, dicom-store-fuzzer, dicom-cfind-ls, etc.) so that protocol logic lives in one place.

INSTALLATION: Copy this file to your Nmap nselib/ directory to replace the built-in minimal dicom.lua: cp dicom.lua /usr/share/nmap/nselib/dicom.lua (or /usr/local/share/nmap/nselib/dicom.lua on macOS) This library is fully backward-compatible with the original: the legacy API (start_connection, send, receive, pdu_header_encode, associate, send_pdata) keeps its exact signatures and behaviour, so the official dicom-ping and dicom-brute scripts run unchanged.

CHANGES vs. the upstream nmap nselib/dicom.lua: * Legacy API preserved verbatim (drop-in compatible). * Added DIMSE service support: C-ECHO, C-STORE, C-FIND, C-GET, C-MOVE. * Added A-ASSOCIATE-RQ builder with multiple presentation contexts, configurable max PDU, and SCP/SCU Role Selection (PS3.7 D.3.3.4) so C-GET can receive C-STORE sub-operations. * Added A-ASSOCIATE-AC parser (accepted contexts, transfer syntaxes, max PDU, implementation identification, accepted roles) for fingerprinting and negotiation. * Added P-DATA-TF fragmentation/reassembly that surfaces the PDV presentation-context ID (needed to answer C-STORE sub-ops correctly). * Added Implicit/Explicit VR LE element encoders/decoders and Part-10 (.dcm) dataset extraction.

OPTIONS: *called_aet - Called Application Entity Title (default: ANY-SCP) *calling_aet - Calling Application Entity Title (default: ECHOSCU)

Author:

  • Paulino Calderon <paulino@calderonpale.com>

Copyright © Same as Nmap--See https://nmap.org/book/man-legal.html

Source: https://svn.nmap.org/nmap/nselib/dicom.lua

Script Arguments

dicom.called_aet

Called Application Entity Title. Default: ANY-SCP

dicom.calling_aet

Calling Application Entity Title. Default: ECHOSCU

Functions

associate (host, port, calling_aet, called_aet)

Legacy: associate(host, port, calling_aet, called_aet)

build_cecho_rq (msg_id)

Build a C-ECHO-RQ command set.

build_cfind_dataset (level, query_tags, transfer_syntax)

Build a C-FIND query dataset for a given Q/R level.

build_cfind_rq (msg_id, sop_class_uid, priority)

Build a C-FIND-RQ command set.

build_cget_dataset (level, query_tags, transfer_syntax)

Build a C-GET query dataset.

build_cget_rq (msg_id, sop_class_uid, priority)

Build a C-GET-RQ command set.

build_cmove_rq (msg_id, sop_class_uid, move_dest, priority)

Build a C-MOVE-RQ command set.

build_cstore_rq (msg_id, sop_class_uid, sop_instance_uid, priority)

Build a C-STORE-RQ command set.

build_cstore_rsp (msg_id_rsp, sop_class_uid, sop_inst_uid, status_code)

Build a C-STORE-RSP command set.

build_pdata_pdus (pctx_id, data, is_command, max_pdu)

Chunk data into one or more P-DATA-TF PDUs (single-stream). Use build_pdata_pdus when you need separate command/dataset PDU lists. Use send_dimse() for the preferred combined approach.

do_release (sock, timeout_s)

Send A-RELEASE-RQ and wait for A-RELEASE-RP (best-effort).

encode_dataset (tags, transfer_syntax)

Encode a dataset (list of tag definitions) using the specified TS. Each tag is {group=N, elem=N, vr="XX", value=...}. Tags are sorted by (group, elem) automatically.

encode_element (group, elem, vr, val, transfer_syntax)

Encode a single element using the specified transfer syntax.

explicit_elem (group, elem, vr, val)

Encode a single DICOM element in Explicit VR Little Endian (PS3.5 §7.1.1). Uses long format for OB/OD/OF/OL/OV/OW/SQ/UC/UN/UR/UT/SV/UV, short format for all other VRs.

generate_fake_uid ()

Generate a plausible fake UID.

get_accepted_ts (pctxs, pctx_id)

Get the negotiated transfer syntax for an accepted presentation context.

get_cert (sock)

Fetch the peer's TLS certificate from a connected TLS socket, if any.

implicit_elem (group, elem, vr, val)

Encode a single DICOM element in Implicit VR Little Endian (PS3.5 §7.1.2).

mk_item (item_type, data)

Build a TLV item: type(1) + reserved(1) + length(2, big-endian) + data.

mk_pres_ctx (pctx_id, sop_class, transfer_uids)

Build a Presentation Context sub-item for A-ASSOCIATE-RQ.

mk_role_item (uid, scu, scp)

Build an SCP/SCU Role Selection sub-item (PS3.7 §D.3.3.4, item type 0x54). Used to request role reversal so the SCU can receive C-STORE sub-operations (required for C-GET).

mk_user_info (max_pdu, roles)

Build User Information sub-item for A-ASSOCIATE-RQ.

new_sock (timeout_s)

Create a new Nmap socket with timeout.

pack_be (val, n)

Pack a big-endian unsigned integer into n bytes.

pack_le (val, n)

Pack a little-endian unsigned integer into n bytes.

pad_str (s, n)

Pad/truncate a string to exactly n bytes (space-padded on right).

parse_assoc_ac (data)

Parse A-ASSOCIATE-AC to extract accepted presentation contexts.

parse_cfind_rsp (data, transfer_syntax)

Parse a C-FIND-RSP from P-DATA-TF PDU bytes.

parse_command_set (cmd_bytes)

Parse Implicit VR LE command set bytes into a keyed table. For group 0x0000, auto-decodes US/UL values; for others, returns raw.

parse_cstore_rsp (data)

Parse a C-STORE-RSP from P-DATA-TF PDU bytes.

parse_dataset (ds_bytes, transfer_syntax)

Parse a dataset using the appropriate decoder for the transfer syntax. If the TS indicates Implicit VR LE, uses the implicit parser; otherwise uses the Explicit VR parser. Includes a heuristic fallback: if explicit parsing is expected but the first element's VR bytes aren't valid ASCII uppercase, falls back to implicit.

parse_dataset_explicit (ds_bytes)

Parse Explicit VR LE dataset bytes into tag -> string table. Handles both short-format and long-format VRs per PS3.5 §7.1.1.

parse_dataset_implicit (ds_bytes)

Parse Implicit VR LE dataset bytes into tag -> string table.

parse_pdata (data)

Extract command bytes and dataset bytes from a single P-DATA-TF PDU.

pdu_header_encode (pdu_type, length)

Encode a DICOM PDU header (6 bytes).

pick_accepted_pctx (pctxs)

Find the first accepted presentation context ID from a pctx map.

read_dcm_dataset (filepath)

Extract the DICOM dataset from a raw .dcm file. Strips 128-byte preamble + "DICM" prefix and File Meta Information.

read_file (path)

Read a file from disk. Returns bytes string or nil, err.

receive (dcm)

Legacy: receive(dcm)

recv_dimse (sock, timeout_s, carry)

Receive a single DIMSE response (command + optional dataset).

recv_pdu (sock, timeout_s, carry)

Receive bytes until one complete PDU is assembled. Accepts an optional carry buffer containing leftover bytes from a previous call (when multiple PDUs arrive in a single TCP segment). Returns the extracted PDU **and** any remaining bytes so the caller can feed them back into the next call.

send (dcm, data)

Legacy: send(dcm, data)

send_dimse (sock, pctx_id, cmd_bytes, ds_bytes, max_pdu)

Send a DIMSE message (command set + optional dataset) as P-DATA-TF PDU(s).

send_pdata (dicom, data)

Legacy: send_pdata(dicom, data)

start_connection (host, port)

Legacy: start_connection(host, port) — opens TCP socket.

tcp_connect (sock, host, port, tls)

Connect a socket. Returns ok, err.

tcp_send (sock, data)

Send all bytes on a socket. Returns ok, err.

tls_enabled ()

Whether DICOM-over-TLS is requested via the global "dicom.tls" script-arg.

write_file (path, data)

Write binary data to a file. Returns true or nil, err.

Tables

COMMAND_FIELD

DIMSE Command Field values (PS3.7 Table E.1-1).

LONG_VRS

VRs that use the long (12-byte) explicit encoding in Explicit VR LE (PS3.5 Table 7.1-1). Membership test: LONG_VRS[vr] is true for these.

PDU_CODES

DICOM Upper Layer PDU type codes (PS3.8 Table 9-1).

PDU_NAMES

Reverse map of PDU_CODES: numeric type code to its name.

QR_LEVEL

Query/Retrieve levels (0008,0052 QueryRetrieveLevel values).

SOP_CLASS

Well-known SOP Class UIDs (storage and Query/Retrieve information models).

STATUS

DIMSE Status codes (PS3.7 Table C.4-1 ff.).

TRANSFER_SYNTAX

Transfer Syntax UIDs (PS3.5).

Functions

associate (host, port, calling_aet, called_aet)

Legacy: associate(host, port, calling_aet, called_aet)

Parameters

host
 
port
 
calling_aet
 
called_aet
 
build_cecho_rq (msg_id)

Build a C-ECHO-RQ command set.

Parameters

msg_id
Message ID

Return value:

Command set bytes (Implicit VR LE)
build_cfind_dataset (level, query_tags, transfer_syntax)

Build a C-FIND query dataset for a given Q/R level.

Parameters

level
QR_LEVEL value ("STUDY", "SERIES", "IMAGE")
query_tags
List of {group, elem, vr, value} tag tables
transfer_syntax
Transfer syntax for encoding (default: Explicit VR LE)

Return value:

Dataset bytes
build_cfind_rq (msg_id, sop_class_uid, priority)

Build a C-FIND-RQ command set.

Parameters

msg_id
Message ID
sop_class_uid
Abstract Syntax UID for the Q/R information model
priority
Priority (default: LOW = 0x0002)

Return value:

Command set bytes (Implicit VR LE)
build_cget_dataset (level, query_tags, transfer_syntax)

Build a C-GET query dataset.

Parameters

level
QR_LEVEL value
query_tags
List of {group, elem, vr, value} tag tables
transfer_syntax
Transfer syntax for encoding

Return value:

Dataset bytes
build_cget_rq (msg_id, sop_class_uid, priority)

Build a C-GET-RQ command set.

Parameters

msg_id
Message ID
sop_class_uid
Abstract Syntax UID for the Q/R information model
priority
Priority (default: LOW = 0x0002)

Return value:

Command set bytes (Implicit VR LE)
build_cmove_rq (msg_id, sop_class_uid, move_dest, priority)

Build a C-MOVE-RQ command set.

Parameters

msg_id
Message ID
sop_class_uid
Abstract Syntax UID for the Q/R information model
move_dest
Move Destination AE Title (max 16 chars, space-padded)
priority
Priority (default: LOW = 0x0002)

Return value:

Command set bytes (Implicit VR LE)
build_cstore_rq (msg_id, sop_class_uid, sop_instance_uid, priority)

Build a C-STORE-RQ command set.

Parameters

msg_id
Message ID
sop_class_uid
Affected SOP Class UID
sop_instance_uid
Affected SOP Instance UID
priority
Priority (default: LOW = 0x0002)

Return value:

Command set bytes (Implicit VR LE)
build_cstore_rsp (msg_id_rsp, sop_class_uid, sop_inst_uid, status_code)

Build a C-STORE-RSP command set.

Parameters

msg_id_rsp
Message ID Being Responded To
sop_class_uid
Affected SOP Class UID
sop_inst_uid
Affected SOP Instance UID
status_code
Status code (default: SUCCESS = 0x0000)

Return value:

Command set bytes (Implicit VR LE)
build_pdata_pdus (pctx_id, data, is_command, max_pdu)

Chunk data into one or more P-DATA-TF PDUs (single-stream). Use build_pdata_pdus when you need separate command/dataset PDU lists. Use send_dimse() for the preferred combined approach.

Parameters

pctx_id
Presentation context ID
data
Raw bytes to send
is_command
true for command set, false for dataset
max_pdu
Max PDU length

Return value:

List of PDU byte strings
do_release (sock, timeout_s)

Send A-RELEASE-RQ and wait for A-RELEASE-RP (best-effort).

Parameters

sock
 
timeout_s
 
encode_dataset (tags, transfer_syntax)

Encode a dataset (list of tag definitions) using the specified TS. Each tag is {group=N, elem=N, vr="XX", value=...}. Tags are sorted by (group, elem) automatically.

Parameters

tags
List of tag tables
transfer_syntax
Transfer syntax UID (default: Explicit VR LE)

Return value:

Encoded dataset bytes
encode_element (group, elem, vr, val, transfer_syntax)

Encode a single element using the specified transfer syntax.

Parameters

group
Group number
elem
Element number
vr
VR string
val
Value
transfer_syntax
Transfer syntax UID (default: Explicit VR LE)

Return value:

Encoded element bytes
explicit_elem (group, elem, vr, val)

Encode a single DICOM element in Explicit VR Little Endian (PS3.5 §7.1.1). Uses long format for OB/OD/OF/OL/OV/OW/SQ/UC/UN/UR/UT/SV/UV, short format for all other VRs.

Parameters

group
Group number
elem
Element number
vr
VR string (2 characters)
val
Value (number for US/UL/SS/SL, string otherwise)

Return value:

Encoded element bytes
generate_fake_uid ()

Generate a plausible fake UID.

get_accepted_ts (pctxs, pctx_id)

Get the negotiated transfer syntax for an accepted presentation context.

Parameters

pctxs
Table from parse_assoc_ac
pctx_id
Presentation context ID

Return value:

Transfer syntax UID string, or nil
get_cert (sock)

Fetch the peer's TLS certificate from a connected TLS socket, if any.

Parameters

sock
Connected Nmap socket (TLS)

Return value:

certificate table (see nmap sslcert), or nil
implicit_elem (group, elem, vr, val)

Encode a single DICOM element in Implicit VR Little Endian (PS3.5 §7.1.2).

Parameters

group
Group number (e.g. 0x0000, 0x0008, 0x0010)
elem
Element number
vr
VR string ("US", "UL", "UI", "CS", "LO", etc.)
val
Value (number for US/UL, string otherwise)

Return value:

Encoded element bytes
mk_item (item_type, data)

Build a TLV item: type(1) + reserved(1) + length(2, big-endian) + data.

Parameters

item_type
 
data
 
mk_pres_ctx (pctx_id, sop_class, transfer_uids)

Build a Presentation Context sub-item for A-ASSOCIATE-RQ.

Parameters

pctx_id
Odd integer 1–255
sop_class
Abstract Syntax UID string
transfer_uids
List of Transfer Syntax UIDs (default: Explicit VR LE + Implicit VR LE)
mk_role_item (uid, scu, scp)

Build an SCP/SCU Role Selection sub-item (PS3.7 §D.3.3.4, item type 0x54). Used to request role reversal so the SCU can receive C-STORE sub-operations (required for C-GET).

Parameters

uid
Abstract Syntax (SOP Class) UID the role applies to
scu
SCU role byte (0 or 1)
scp
SCP role byte (0 or 1)

Return value:

Encoded role selection sub-item bytes
mk_user_info (max_pdu, roles)

Build User Information sub-item for A-ASSOCIATE-RQ.

Parameters

max_pdu
Max PDU length to advertise
roles
(optional) list of {uid=<SOP Class UID>, scu=bool, scp=bool} role-selection requests to append (for C-GET, request scp=true on the storage SOP classes you are willing to receive).
new_sock (timeout_s)

Create a new Nmap socket with timeout.

Parameters

timeout_s
 
pack_be (val, n)

Pack a big-endian unsigned integer into n bytes.

Parameters

val
 
n
 
pack_le (val, n)

Pack a little-endian unsigned integer into n bytes.

Parameters

val
 
n
 
pad_str (s, n)

Pad/truncate a string to exactly n bytes (space-padded on right).

Parameters

s
 
n
 
parse_assoc_ac (data)

Parse A-ASSOCIATE-AC to extract accepted presentation contexts.

Parameters

data
Raw A-ASSOCIATE-AC PDU bytes

Return value:

Table { pctxs = {pctx_id -> {accepted, transfer_syntax}}, max_pdu = n }
parse_cfind_rsp (data, transfer_syntax)

Parse a C-FIND-RSP from P-DATA-TF PDU bytes.

Parameters

data
Raw PDU bytes
transfer_syntax
Transfer syntax for dataset decoding (default: Explicit VR LE)

Return value:

status (int), dataset_elements (table or nil), cmd_elements (table)
parse_command_set (cmd_bytes)

Parse Implicit VR LE command set bytes into a keyed table. For group 0x0000, auto-decodes US/UL values; for others, returns raw.

Parameters

cmd_bytes
Raw command set bytes

Return value:

Table { "GGGG,EEEE" -> {group, elem, raw, value} }
parse_cstore_rsp (data)

Parse a C-STORE-RSP from P-DATA-TF PDU bytes.

Parameters

data
Raw PDU bytes

Return value:

status_code (int), error_comment (string)
parse_dataset (ds_bytes, transfer_syntax)

Parse a dataset using the appropriate decoder for the transfer syntax. If the TS indicates Implicit VR LE, uses the implicit parser; otherwise uses the Explicit VR parser. Includes a heuristic fallback: if explicit parsing is expected but the first element's VR bytes aren't valid ASCII uppercase, falls back to implicit.

Parameters

ds_bytes
Raw dataset bytes
transfer_syntax
Transfer syntax UID

Return value:

Table { "GGGG,EEEE" -> cleaned_string }
parse_dataset_explicit (ds_bytes)

Parse Explicit VR LE dataset bytes into tag -> string table. Handles both short-format and long-format VRs per PS3.5 §7.1.1.

Parameters

ds_bytes
Raw dataset bytes

Return value:

Table { "GGGG,EEEE" -> cleaned_string }
parse_dataset_implicit (ds_bytes)

Parse Implicit VR LE dataset bytes into tag -> string table.

Parameters

ds_bytes
Raw dataset bytes

Return value:

Table { "GGGG,EEEE" -> cleaned_string }
parse_pdata (data)

Extract command bytes and dataset bytes from a single P-DATA-TF PDU.

Parameters

data
Raw P-DATA-TF PDU bytes

Return value:

cmd_bytes, dataset_bytes, cmd_complete, ds_complete, pctx_id cmd_complete: true if the last command PDV had the "last fragment" bit set ds_complete: true if the last dataset PDV had the "last fragment" bit set pctx_id: presentation context ID of the PDV(s) (command PDV preferred), needed to reply on the correct context (e.g. C-STORE-RSP during C-GET sub-operations). nil if no PDV was seen.
pdu_header_encode (pdu_type, length)

Encode a DICOM PDU header (6 bytes).

Parameters

pdu_type
PDU type byte
length
Length of the PDU body

Return value:

status (bool), header_or_err (string)
pick_accepted_pctx (pctxs)

Find the first accepted presentation context ID from a pctx map.

Parameters

pctxs
Table from parse_assoc_ac {pctx_id -> {accepted, transfer_syntax}}

Return value:

pctx_id (int) or nil
read_dcm_dataset (filepath)

Extract the DICOM dataset from a raw .dcm file. Strips 128-byte preamble + "DICM" prefix and File Meta Information.

Parameters

filepath
Path to .dcm file

Return value:

dataset_bytes, sop_class_uid, sop_instance_uid, transfer_syntax_uid, err
read_file (path)

Read a file from disk. Returns bytes string or nil, err.

Parameters

path
 
receive (dcm)

Legacy: receive(dcm)

Parameters

dcm
 
recv_dimse (sock, timeout_s, carry)

Receive a single DIMSE response (command + optional dataset).

The server may send the command and dataset in a single P-DATA-TF PDU (with two PDV items) or in separate P-DATA-TF PDUs. This function handles both cases by checking CommandDataSetType (0000,0800) in the command set: if a dataset is expected but not yet received, it reads the next PDU to obtain it.

Accepts and returns a carry buffer so the caller can chain calls without losing bytes when multiple PDUs arrive in one TCP segment.

Parameters

sock
Nmap socket
timeout_s
Timeout in seconds
carry
(optional) leftover bytes from previous recv_pdu

Return value:

pdu_type (int or nil), cmd_elems (table), ds_bytes (string), raw_pdu (string), remaining (string – leftover bytes for next call), pctx_id (int or nil – presentation context the message arrived on; needed to reply on the correct context during C-GET sub-operations)
recv_pdu (sock, timeout_s, carry)

Receive bytes until one complete PDU is assembled. Accepts an optional carry buffer containing leftover bytes from a previous call (when multiple PDUs arrive in a single TCP segment). Returns the extracted PDU **and** any remaining bytes so the caller can feed them back into the next call.

Parameters

sock
Nmap socket
timeout_s
Timeout in seconds (used for error classification)
carry
(optional) leftover bytes from a previous recv_pdu call

Return value:

ok (bool), data_or_err (string), remaining (string – leftover bytes, empty on error)
send (dcm, data)

Legacy: send(dcm, data)

Parameters

dcm
 
data
 
send_dimse (sock, pctx_id, cmd_bytes, ds_bytes, max_pdu)

Send a DIMSE message (command set + optional dataset) as P-DATA-TF PDU(s).

When both the command and dataset fit within a single PDU, they are combined into one P-DATA-TF PDU with two PDV items (matching the behaviour of DCMTK and pynetdicom). For larger payloads, the dataset is fragmented across multiple PDUs.

Command sets are always Implicit VR LE. The dataset must already be encoded in the negotiated transfer syntax before calling this function.

Parameters

sock
Nmap socket
pctx_id
Accepted presentation context ID
cmd_bytes
Command set bytes (Implicit VR LE)
ds_bytes
Dataset bytes (already encoded in negotiated TS), or nil
max_pdu
Max PDU size (use server's negotiated value)

Return value:

ok (bool), err (string or nil)
send_pdata (dicom, data)

Legacy: send_pdata(dicom, data)

Parameters

dicom
 
data
 
start_connection (host, port)

Legacy: start_connection(host, port) — opens TCP socket.

Parameters

host
 
port
 
tcp_connect (sock, host, port, tls)

Connect a socket. Returns ok, err.

Parameters

sock
Nmap socket
host
Host object
port
Port object (or number)
tls
true = TLS, false = plain TCP, nil = honor the dicom.tls arg
tcp_send (sock, data)

Send all bytes on a socket. Returns ok, err.

Parameters

sock
 
data
 
tls_enabled ()

Whether DICOM-over-TLS is requested via the global "dicom.tls" script-arg.

Return value:

true if dicom.tls=true was supplied on the command line
write_file (path, data)

Write binary data to a file. Returns true or nil, err.

Parameters

path
 
data
 

Tables

COMMAND_FIELD

DIMSE Command Field values (PS3.7 Table E.1-1).

LONG_VRS

VRs that use the long (12-byte) explicit encoding in Explicit VR LE (PS3.5 Table 7.1-1). Membership test: LONG_VRS[vr] is true for these.

PDU_CODES

DICOM Upper Layer PDU type codes (PS3.8 Table 9-1).

PDU_NAMES

Reverse map of PDU_CODES: numeric type code to its name.

QR_LEVEL

Query/Retrieve levels (0008,0052 QueryRetrieveLevel values).

SOP_CLASS

Well-known SOP Class UIDs (storage and Query/Retrieve information models).

STATUS

DIMSE Status codes (PS3.7 Table C.4-1 ff.).

TRANSFER_SYNTAX

Transfer Syntax UIDs (PS3.5).