Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-hl7.c
Line
Count
Source
1
/* packet-hl7.c
2
 * Routines for Health Level 7 (HL7) dissection: HL7 messages wrapped in the
3
 * Minimal Lower Layer Protocol (MLLP) as specified in 'HL7 Implementation
4
 * Support Guide' version 2.3, 'Appendix C: Lower Layer Protocols',
5
 * section C.4. See page 306-308:
6
 * https://web.archive.org/web/20031005202343/http://www.hl7.org/Special/IG/final.pdf
7
 *
8
 * Copyright 2016 Francesco Fondelli <francesco dot fondelli, gmail dot com>
9
 *
10
 * Wireshark - Network traffic analyzer
11
 * By Gerald Combs <gerald@wireshark.org>
12
 * Copyright 1998 Gerald Combs
13
 *
14
 * SPDX-License-Identifier: GPL-2.0-or-later
15
 *
16
 * TODO:
17
 * - HL7 messages are most commonly strings with strict ASCII encoding.
18
 *   However, Unicode or UTF-8 encodings are possible (?). This dissector
19
 *   lacks support for non-ASCII encodings.
20
 * - Component and sub-component expansion (not sure is necessary).
21
 * - Handling delimiter characters in data, i.e. escape sequences support.
22
 * - Add event type human readable strings.
23
 * - Improve heuristic detection logic: can a message start with FHS? HLLB
24
 *   encapsulation? Some common TCP ports besides the one assigned by IANA?
25
 * - Use GHashTable for lookup segment type description instead of linear
26
 *   search.
27
 */
28
29
#include "config.h"
30
31
#include <epan/packet.h>
32
#include <epan/addr_resolv.h>
33
#include <epan/conversation.h>
34
#include <epan/expert.h>
35
#include <epan/prefs.h>
36
#include <epan/charsets.h>
37
38
void proto_register_hl7(void);
39
void proto_reg_handoff_hl7(void);
40
41
/* 2575 is registered at IANA for HL7 */
42
16
#define TCP_PORT_HL7 2575
43
2.15k
#define LLP_SOB 0x0B /* Start Of Block byte */
44
6
#define LLP_EOB 0x1C0D /* End Of Block byte + \r */
45
46
struct msh {                    // typical/default values
47
    char field_separator;       // '|'
48
    char component_separator;   // '^'
49
    char repetition_separator;  // '~'
50
    char escape_character;      // '\'
51
    char subcomponent_separator;// '&'
52
    char message_type[4];
53
    char trigger_event[4];
54
};
55
56
static dissector_handle_t hl7_handle;
57
58
static int proto_hl7;
59
60
static int hf_hl7_raw;
61
static int hf_hl7_raw_segment;
62
static int hf_hl7_llp_sob;
63
static int hf_hl7_llp_eob;
64
static int hf_hl7_message_type;
65
static int hf_hl7_event_type;
66
static int hf_hl7_segment;
67
static int hf_hl7_field;
68
69
static int ett_hl7;
70
static int ett_hl7_segment;
71
72
static expert_field ei_hl7_malformed;
73
74
/* FF: global_hl7_raw determines whether we are going to display
75
 * the raw text of the HL7 message (like SIP and MEGACO dissectors) */
76
static bool global_hl7_raw;
77
78
/* FF: global_hl7_llp determines whether we are going to display
79
 * the LLP block markers */
80
static bool global_hl7_llp;
81
82
/* as per Health Level Seven, Version 2.6, appendix A */
83
static const string_string hl7_msg_type_vals[] = {
84
    { "ACK", "General acknowledgment" },
85
    { "ADT", "Admit Discharge Transfer" },
86
    { "BAR", "Add/change billing account" },
87
    { "BPS", "Blood product dispense status" },
88
    { "BRP", "Blood product dispense status acknowledgement" },
89
    { "BRT", "Blood product transfusion/disposition acknowledgement" },
90
    { "BTS", "Blood product transfusion/disposition" },
91
    { "CRM", "Clinical study registration" },
92
    { "CSU", "Unsolicited study data" },
93
    { "DFT", "Detail financial transactions" },
94
    { "EAC", "Automated equipment command" },
95
    { "EAN", "Automated equipment notification" },
96
    { "EAR", "Automated equipment response" },
97
    { "EHC", "Health Care Invoice" },
98
    { "ESR", "Automated equipment status update acknowledgment" },
99
    { "ESU", "Automated equipment status update" },
100
    { "INR", "Automated equipment inventory request" },
101
    { "INU", "Automated equipment inventory update" },
102
    { "LSR", "Automated equipment log/service request" },
103
    { "LSU", "Automated equipment log/service update" },
104
    { "MDM", "Medical document management" },
105
    { "MFN", "Master files notification" },
106
    { "NMD", "Application management data" },
107
    { "NMQ", "Application management query" },
108
    { "OMB", "Blood product order" },
109
    { "OMD", "Dietary order" },
110
    { "OMG", "General clinical order" },
111
    { "OMI", "Imaging order" },
112
    { "OML", "Laboratory order" },
113
    { "OMN", "Non-stock requisition order" },
114
    { "OMP", "Pharmacy/treatment order" },
115
    { "OMS", "Stock requisition order" },
116
    { "OPL", "Population/Location-Based Laboratory Order" },
117
    { "OPR", "Population/Location-Based Laboratory Order Acknowledgment" },
118
    { "OPU", "Unsolicited Population/Location-Based Laboratory Observation" },
119
    { "ORB", "Blood product order acknowledgement" },
120
    { "ORD", "Dietary order acknowledgment" },
121
    { "ORF", "Query for results of observation" },
122
    { "ORG", "General clinical order acknowledgment" },
123
    { "ORI", "Imaging order acknowledgement" },
124
    { "ORL", "Laboratory acknowledgment (unsolicited)" },
125
    { "ORM", "Pharmacy/treatment order" },
126
    { "ORN", "Non-stock requisition - General order acknowledgment" },
127
    { "ORP", "Pharmacy/treatment order acknowledgment" },
128
    { "ORR", "General order response message response to any ORM" },
129
    { "ORS", "Stock requisition - Order acknowledgment" },
130
    { "ORU", "Unsolicited transmission of an observation" },
131
    { "OSQ", "Query response for order status" },
132
    { "OUL", "Unsolicited laboratory observation" },
133
    { "PEX", "Product experience" },
134
    { "PGL", "Patient goal" },
135
    { "PIN", "Patient insurance information" },
136
    { "PMU", "Add personnel record" },
137
    { "PPG", "Patient pathway (goal-oriented)" },
138
    { "PPP", "Patient pathway (problem-oriented)" },
139
    { "PPR", "Patient problem" },
140
    { "PPT", "Patient pathway goal-oriented response" },
141
    { "PPV", "Patient goal response" },
142
    { "PRR", "Patient problem response" },
143
    { "PTR", "Patient pathway problem-oriented response" },
144
    { "QBP", "Query by parameter" },
145
    { "QCN", "Cancel query" },
146
    { "QRY", "Query, original mode" },
147
    { "QSB", "Create subscription" },
148
    { "QSX", "Cancel subscription/acknowledge" },
149
    { "QVR", "Query for previous events" },
150
    { "RAR", "Pharmacy/treatment administration information" },
151
    { "RAS", "Pharmacy/treatment administration" },
152
    { "RDE", "Pharmacy/treatment encoded order" },
153
    { "RDS", "Pharmacy/treatment dispense" },
154
    { "RDY", "Display based response" },
155
    { "REF", "Patient referral" },
156
    { "RER", "Pharmacy/treatment encoded order information" },
157
    { "RGV", "Pharmacy/treatment give" },
158
    { "ROR", "Pharmacy/treatment order response" },
159
    { "RQA", "Request patient authorization" },
160
    { "RQC", "Request clinical information" },
161
    { "RQI", "Request patient information" },
162
    { "RQP", "Request patient demographics" },
163
    { "RRA", "Pharmacy/treatment administration acknowledgment" },
164
    { "RRD", "Pharmacy/treatment dispense acknowledgment" },
165
    { "RRE", "Pharmacy/treatment encoded order acknowledgment" },
166
    { "RRG", "Pharmacy/treatment give acknowledgment" },
167
    { "RSP", "Segment pattern response" },
168
    { "RTB", "Tabular response" },
169
    { "SCN", "Notification of Anti-Microbial Device Cycle Data" },
170
    { "SDN", "Notification of Anti-Microbial Device Data" },
171
    { "SDR", "Sterilization anti-microbial device data request" },
172
    { "SIU", "Schedule information unsolicited" },
173
    { "SLN", "Notification of New Sterilization Lot" },
174
    { "SLR", "Sterilization lot request" },
175
    { "SMD", "Sterilization anti-microbial device cycle data request" },
176
    { "SQM", "Schedule query" },
177
    { "SRM", "Schedule request" },
178
    { "SSR", "Specimen status request" },
179
    { "SSU", "Specimen status update" },
180
    { "STC", "Notification of Sterilization Configuration" },
181
    { "STI", "Sterilization item request" },
182
    { "SUR", "Summary product experience report" },
183
    { "TCR", "Automated equipment test code settings request" },
184
    { "TCU", "Automated equipment test code settings update" },
185
    { "VXQ", "Query for vaccination record" },
186
    { "VXR", "Vaccination record response" },
187
    { "VXU", "Unsolicited vaccination record update" },
188
    { "VXX", "Response for vaccination query with multiple PID matches" },
189
    { NULL, NULL }
190
};
191
192
/* as per Health Level Seven, Version 2.6, appendix A */
193
static const string_string hl7_seg_type_vals[] = {
194
    { "ABS", "Abstract" },
195
    { "ACC", "Accident" },
196
    { "ADD", "Addendum" },
197
    { "ADJ", "Adjustment" },
198
    { "AFF", "Professional Affiliation" },
199
    { "AIG", "Appointment Information - General Resource" },
200
    { "AIL", "Appointment Information - Location Resource" },
201
    { "AIP", "Appointment Information - Personnel Resource" },
202
    { "AIS", "Appointment Information" },
203
    { "AL1", "Patient Allergy Information" },
204
    { "APR", "Appointment Preferences" },
205
    { "ARQ", "Appointment Request" },
206
    { "ARV", "Access Restriction" },
207
    { "AUT", "Authorization Information" },
208
    { "BHS", "Batch Header" },
209
    { "BLC", "Blood Code" },
210
    { "BLG", "Billing" },
211
    { "BPO", "Blood product order" },
212
    { "BPX", "Blood product dispense status" },
213
    { "BTS", "Batch Trailer" },
214
    { "BTX", "Blood Product Transfusion/Disposition" },
215
    { "CDM", "Charge Description Master" },
216
    { "CER", "Certificate Detail" },
217
    { "CM0", "Clinical Study Master" },
218
    { "CM1", "Clinical Study Phase Master" },
219
    { "CM2", "Clinical Study Schedule Master" },
220
    { "CNS", "Clear Notification" },
221
    { "CON", "Consent Segment" },
222
    { "CSP", "Clinical Study Phase" },
223
    { "CSR", "Clinical Study Registration" },
224
    { "CSS", "Clinical Study Data Schedule Segment" },
225
    { "CTD", "Contact Data" },
226
    { "CTI", "Clinical Trial Identification" },
227
    { "DB1", "Disability" },
228
    { "DG1", "Diagnosis" },
229
    { "DMI", "DRG Master File Information" },
230
    { "DRG", "Diagnosis Related Group" },
231
    { "DSC", "Continuation Pointer" },
232
    { "DSP", "Display Data" },
233
    { "ECD", "Equipment Command" },
234
    { "ECR", "Equipment Command Response" },
235
    { "EDE", "Encapsulated Data (wrong segment)" },
236
    { "EDU", "Educational Detail" },
237
    { "EQP", "Equipment/log Service" },
238
    { "EQU", "Equipment Detail" },
239
    { "ERR", "Error" },
240
    { "EVN", "Event Type" },
241
    { "FAC", "Facility" },
242
    { "FHS", "File Header" },
243
    { "FT1", "Financial Transaction" },
244
    { "FTS", "File Trailer" },
245
    { "GOL", "Goal Detail" },
246
    { "GP1", "Grouping/Reimbursement - Visit" },
247
    { "GP2", "Grouping/Reimbursement - Procedure Line Item" },
248
    { "GT1", "Guarantor" },
249
    { "IAM", "Patient Adverse Reaction Information" },
250
    { "IIM", "Inventory Item Master" },
251
    { "ILT", "Material Lot" },
252
    { "IN1", "Insurance" },
253
    { "IN2", "Insurance Additional Information" },
254
    { "IN3", "Insurance Additional Information, Certification" },
255
    { "INV", "Inventory Detail" },
256
    { "IPC", "Imaging Procedure Control Segment" },
257
    { "IPR", "Invoice Processing Results" },
258
    { "ISD", "Interaction Status Detail" },
259
    { "ITM", "Material Item" },
260
    { "IVC", "Invoice Segment" },
261
    { "IVT", "Material Location" },
262
    { "LAN", "Language Detail" },
263
    { "LCC", "Location Charge Code" },
264
    { "LCH", "Location Characteristic" },
265
    { "LDP", "Location Department" },
266
    { "LOC", "Location Identification" },
267
    { "LRL", "Location Relationship" },
268
    { "MFA", "Master File Acknowledgment" },
269
    { "MFE", "Master File Entry" },
270
    { "MFI", "Master File Identification" },
271
    { "MRG", "Merge Patient Information" },
272
    { "MSA", "Message Acknowledgment" },
273
    { "MSH", "Message Header" },
274
    { "NCK", "System Clock" },
275
    { "NDS", "Notification Detail" },
276
    { "NK1", "Next of Kin - Associated Parties" },
277
    { "NPU", "Bed Status Update" },
278
    { "NSC", "Application Status Change" },
279
    { "NST", "Application control level statistics" },
280
    { "NTE", "Notes and Comments" },
281
    { "OBR", "Observation Request" },
282
    { "OBX", "Observation/Result" },
283
    { "ODS", "Dietary Orders, Supplements, and Preferences" },
284
    { "ODT", "Diet Tray Instructions" },
285
    { "OM1", "General Segment" },
286
    { "OM2", "Numeric Observation" },
287
    { "OM3", "Categorical Service/Test/Observation" },
288
    { "OM4", "Observations that Require Specimens" },
289
    { "OM5", "Observation Batteries (Sets)" },
290
    { "OM6", "Observations that are Calculated from Other" },
291
    { "OM7", "Additional Basic Attributes" },
292
    { "ORC", "Common Order" },
293
    { "ORG", "Practitioner Organization Unit" },
294
    { "OVR", "Override Segment" },
295
    { "PCE", "Patient Charge Cost Center Exceptions" },
296
    { "PCR", "Possible Causal Relationship" },
297
    { "PD1", "Patient Additional Demographic" },
298
    { "PDA", "Patient Death and Autopsy" },
299
    { "PDC", "Product Detail Country" },
300
    { "PEO", "Product Experience Observation" },
301
    { "PES", "Product Experience Sender" },
302
    { "PID", "Patient Identification" },
303
    { "PKG", "Item Packaging" },
304
    { "PMT", "Payment Information" },
305
    { "PR1", "Procedures" },
306
    { "PRA", "Practitioner Detail" },
307
    { "PRB", "Problem Details" },
308
    { "PRC", "Pricing" },
309
    { "PRD", "Provider Data" },
310
    { "PSG", "Product/Service Group" },
311
    { "PSH", "Product Summary Header" },
312
    { "PSL", "Product/Service Line Item" },
313
    { "PSS", "Product/Service Section" },
314
    { "PTH", "Pathway" },
315
    { "PV1", "Patient Visit" },
316
    { "PV2", "Patient Visit - Additional Information" },
317
    { "PYE", "Payee Information" },
318
    { "QAK", "Query Acknowledgment" },
319
    { "QID", "Query Identification" },
320
    { "QPD", "Query Parameter Definition" },
321
    { "QRD", "Original-Style Query Definition" },
322
    { "QRF", "Original style query filter" },
323
    { "QRI", "Query Response Instance" },
324
    { "RCP", "Response Control Parameter" },
325
    { "RDF", "Table Row Definition" },
326
    { "RDT", "Table Row Data" },
327
    { "REL", "Clinical Relationship Segment" },
328
    { "RF1", "Referral Information" },
329
    { "RFI", "Request for Information" },
330
    { "RGS", "Resource Group" },
331
    { "RMI", "Risk Management Incident" },
332
    { "ROL", "Role" },
333
    { "RQ1", "Requisition Detail-1" },
334
    { "RQD", "Requisition Detail" },
335
    { "RXA", "Pharmacy/Treatment Administration" },
336
    { "RXC", "Pharmacy/Treatment Component Order" },
337
    { "RXD", "Pharmacy/Treatment Dispense" },
338
    { "RXE", "Pharmacy/Treatment Encoded Order" },
339
    { "RXG", "Pharmacy/Treatment Give" },
340
    { "RXO", "Pharmacy/Treatment Order" },
341
    { "RXR", "Pharmacy/Treatment Route" },
342
    { "SAC", "Specimen Container detail" },
343
    { "SCD", "Anti-Microbial Cycle Data" },
344
    { "SCH", "Scheduling Activity Information" },
345
    { "SCP", "Sterilizer Configuration Notification (Anti-Microbial Devices)" },
346
    { "SDD", "Sterilization Device Data" },
347
    { "SFT", "Software Segment" },
348
    { "SID", "Substance Identifier" },
349
    { "SLT", "Sterilization Lot" },
350
    { "SPM", "Specimen" },
351
    { "STF", "Staff Identification" },
352
    { "STZ", "Sterilization Parameter" },
353
    { "TCC", "Test Code Configuration" },
354
    { "TCD", "Test Code Detail" },
355
    { "TQ1", "Timing/Quantity" },
356
    { "TQ2", "Timing/Quantity Relationship" },
357
    { "TXA", "Transcription Document Header" },
358
    { "UAC", "User Authentication Credential Segment" },
359
    { "UB1", "UB82" },
360
    { "UB2", "UB92 Data" },
361
    { "URD", "Results/update Definition" },
362
    { "URS", "Unsolicited Selection" },
363
    { "VAR", "Variance" },
364
    { "VND", "Purchasing Vendor" },
365
    { NULL, NULL }
366
};
367
368
/* as per Health Level Seven, Version 2.6, appendix A */
369
static const string_string hl7_event_type_vals[] = {
370
    { "A01", "Admit/visit notification" },
371
    { "A02", "Transfer a patient" },
372
    { "A03", "Discharge/end visit" },
373
    { "A04", "Register a patient" },
374
    { "A05", "Pre-admit a patient" },
375
    { "A06", "Change an outpatient to an inpatient" },
376
    { "A07", "Change an inpatient to an outpatient" },
377
    { "A08", "Update patient information" },
378
    { "A09", "Patient departing - tracking" },
379
    { "A10", "Patient arriving - tracking" },
380
    { "A11", "Cancel admit/visit notification" },
381
    { "A12", "Cancel transfer" },
382
    { "A13", "Cancel discharge/end visit" },
383
    { "A14", "Pending admit" },
384
    { "A15", "Pending transfer" },
385
    { "A16", "Pending discharge" },
386
    { "A17", "Swap patients" },
387
    { "A18", "Merge patient information" },
388
    { "A19", "Patient query" },
389
    { "A20", "Bed status update" },
390
    { "A21", "Patient goes on a \"leave of absence\"" },
391
    { "A22", "Patient returns from a \"leave of absence\"" },
392
    { "A23", "Delete a patient record" },
393
    { "A24", "Link patient information" },
394
    { "A25", "Cancel pending discharge " },
395
    { "A26", "Cancel pending transfer" },
396
    { "A27", "Cancel pending admit" },
397
    { "A28", "Add person information" },
398
    { "A29", "Delete person information" },
399
    { "A30", "Merge person information" },
400
    { "A31", "Update person information" },
401
    { "A32", "Cancel patient arriving" },
402
    { "A33", "Cancel patient departing" },
403
    { "A34", "Merge patient information - patient ID only" },
404
    { "A35", "Merge patient information - account number only" },
405
    { "A36", "Merge patient information - patient ID and account number" },
406
    { "A37", "Unlink patient information" },
407
    { "A38", "Cancel pre-admit" },
408
    { "A39", "Merge person - patient ID" },
409
    { "A40", "Merge patient - patient identifier list" },
410
    { "A41", "Merge account - patient account number" },
411
    { "A42", "Merge visit - visit number" },
412
    { "A43", "Move patient information - patient identifier list" },
413
    { "A44", "Move account information - patient account number" },
414
    { "A45", "Move visit information - visit number" },
415
    { "A46", "Change patient ID" },
416
    { "A47", "Change patient identifier list" },
417
    { "A48", "Change alternate patient ID" },
418
    { "A49", "Change patient account number" },
419
    { "A50", "Change visit number" },
420
    { "A51", "Change alternate visit ID" },
421
    { "A52", "Cancel leave of absence for a patient" },
422
    { "A53", "Cancel patient returns from a leave of absence" },
423
    { "A54", "Change attending doctor" },
424
    { "A55", "Cancel change attending doctor" },
425
    { "A60", "Update allergy information" },
426
    { "A61", "Change consulting doctor" },
427
    { "A62", "Cancel change consulting doctor" },
428
    { "B01", "Add personnel record" },
429
    { "B02", "Update personnel record" },
430
    { "B03", "Delete personnel re cord" },
431
    { "B04", "Active practicing person" },
432
    { "B05", "Deactivate practicing person" },
433
    { "B06", "Terminate practicing person" },
434
    { "B07", "Grant Certificate/Permission" },
435
    { "B08", "Revoke Certificate/Permission" },
436
    { "C01", "Register a patient on a clinical trial" },
437
    { "C02", "Cancel a patient registration on clinical trial" },
438
    { "C03", "Correct/update registration information" },
439
    { "C04", "Patient has gone off a clinical trial" },
440
    { "C05", "Patient enters phase of clinical trial" },
441
    { "C06", "Cancel patient entering a phase" },
442
    { "C07", "Correct/update phase information" },
443
    { "C08", "Patient has gone off phase of clinical trial" },
444
    { "C09", "Automated time intervals for reporting" },
445
    { "C10", "Patient completes the clinical trial" },
446
    { "C11", "Patient completes a phase of the clinical trial" },
447
    { "C12", "Update/correction of patient order/result information" },
448
    { "E01", "Submit HealthCare Services Invoice" },
449
    { "E02", "Cancel HealthCare Services Invoice" },
450
    { "E03", "HealthCare Services Invoice Status" },
451
    { "E04", "Re-Assess HealthCare Services Invoice Request" },
452
    { "E10", "Edit/Adjudication Results" },
453
    { "E12", "Request Additional Information" },
454
    { "E13", "Additional Information Response" },
455
    { "E15", "Payment/Remittance Advice" },
456
    { "E20", "Submit Authorization Request" },
457
    { "E21", "Cancel Authorization Request" },
458
    { "E22", "Authorization Request Status" },
459
    { "E24", "Authorization Response " },
460
    { "E30", "Submit Health Document related to Authorization Request" },
461
    { "E31", "Cancel Health Document related to Authorization Request" },
462
    { "I01", "Request for insurance information" },
463
    { "I02", "Request/receipt of patient selection display list" },
464
    { "I03", "Request/receipt of patient selection list" },
465
    { "I04", "Request for patient demographic data" },
466
    { "I05", "Request for patient clinical information" },
467
    { "I06", "Request/receipt of clinical data listing" },
468
    { "I07", "Unsolicited insurance information" },
469
    { "I08", "Request for treatment authorization information" },
470
    { "I09", "Request for modification to an authorization" },
471
    { "I10", "Request for resubmission of an authorization" },
472
    { "I11", "Request for cancellation of an authorization" },
473
    { "I12", "Patient referral" },
474
    { "I13", "Modify patient referral" },
475
    { "I14", "Cancel patient referral" },
476
    { "I15", "Request patient referral status" },
477
    { "J01", "Cancel query/acknowledge message" },
478
    { "J02", "Cancel subscription/acknowledge message" },
479
    { "K11", "Segment pattern response in response to QBP^Q11" },
480
    { "K13", "Tabular response in response to QBP^Q13" },
481
    { "K15", "Display response in response to QBP^Q15" },
482
    { "K21", "Get person demographics response" },
483
    { "K22", "Find candidates response" },
484
    { "K23", "Get corresponding identifiers response" },
485
    { "K24", "Allocate identifiers response" },
486
    { "K25", "Personnel Information by Segment Response" },
487
    { "K31", "Dispense History Response" },
488
    { "M01", "Master file not otherwise specified" },
489
    { "M02", "Master file - staff practitioner " },
490
    { "M03", "Master file - test/observation" },
491
    { "M04", "Master files charge description" },
492
    { "M05", "Patient location master file" },
493
    { "M06", "Clinical study with phases and schedules master file" },
494
    { "M07", "Clinical study without phases but with schedules master file" },
495
    { "M08", "Test/observation (numeric) master file" },
496
    { "M09", "Test/observation (categorical) master file" },
497
    { "M10", "Test /observation batteries master file" },
498
    { "M11", "Test/calculated observations master file" },
499
    { "M12", "Master file notification message" },
500
    { "M13", "Master file notification - general" },
501
    { "M14", "Master file notification - site defined" },
502
    { "M15", "Inventory item master file notification" },
503
    { "M16", "Master File Notification Inventory Item Enhanced" },
504
    { "M17", "Master File Message" },
505
    { "N01", "Application management query message" },
506
    { "N02", "Application management data message (unsolicited)" },
507
    { "O01", "Order message" },
508
    { "O02", "Order response" },
509
    { "O03", "Diet order" },
510
    { "O04", "Diet order acknowledgment" },
511
    { "O05", "Stock requisition order" },
512
    { "O06", "Stock requisition acknowledgment" },
513
    { "O07", "Non-stock requisition order" },
514
    { "O08", "Non-stock requisition acknowledgment" },
515
    { "O09", "Pharmacy/treatment order" },
516
    { "O10", "Pharmacy/treatment order acknowledgment" },
517
    { "O11", "Pharmacy/treatment encoded order" },
518
    { "O12", "Pharmacy/treatment encoded order acknowledgment " },
519
    { "O13", "Pharmacy/treatment dispense" },
520
    { "O14", "Pharmacy/treatment dispense acknowledgment" },
521
    { "O15", "Pharmacy/treatment give" },
522
    { "O16", "Pharmacy/treatment give acknowledgment" },
523
    { "O17", "Pharmacy/treatment administration" },
524
    { "O18", "Pharmacy/treatment administration acknowledgment" },
525
    { "O19", "General clinical order" },
526
    { "O20", "General clinical order response" },
527
    { "O21", "Laboratory order" },
528
    { "O22", "General laboratory order response message to any OML" },
529
    { "O23", "Imaging order" },
530
    { "O24", "Imaging order response message to any OMI" },
531
    { "O25", "Pharmacy/treatment refill authorization request" },
532
    { "O26", "Pharmacy/Treatment Refill Authorization Acknowledgement" },
533
    { "O27", "Blood product order" },
534
    { "O28", "Blood product order acknowledgment" },
535
    { "O29", "Blood product dispense status" },
536
    { "O30", "Blood product dispense status acknowledgment" },
537
    { "O31", "Blood product transfusion/disposition" },
538
    { "O32", "Blood product transfusion/disposition acknowledgment" },
539
    { "O33", "Laboratory order for multiple orders related to a single specimen" },
540
    { "O34", "Laboratory order response message to a multiple order related to single specimen OML" },
541
    { "O35", "Laboratory order for multiple orders related to a single container of a specimen" },
542
    { "O36", "Laboratory order response message to a single container of a specimen OML" },
543
    { "O37", "Population/Location-Based Laboratory Order Message" },
544
    { "O38", "Population/Location-Based Laboratory Order Acknowledgment Message" },
545
    { "P01", "Add patient accounts" },
546
    { "P02", "Purge patient accounts" },
547
    { "P03", "Post detail financial transaction " },
548
    { "P04", "Generate bill and A/R statements" },
549
    { "P05", "Update account" },
550
    { "P06", "End account" },
551
    { "P07", "Unsolicited initial individual product experience report" },
552
    { "P08", "Unsolicited update individual product experience report" },
553
    { "P09", "Summary product experience report" },
554
    { "P10", "Transmit Ambulatory Payment Classification" },
555
    { "P11", "Post Detail Financial Transactions" },
556
    { "P12", "Update Diagnosis/Procedure" },
557
    { "PC1", "PC/problem add" },
558
    { "PC2", "PC/problem update" },
559
    { "PC3", "PC/problem delete" },
560
    { "PC4", "PC/problem query" },
561
    { "PC5", "PC/problem response" },
562
    { "PC6", "PC/goal add" },
563
    { "PC7", "PC/goal update" },
564
    { "PC8", "PC/goal delete" },
565
    { "PC9", "PC/goal query" },
566
    { "PCA", "PC/goal response" },
567
    { "PCB", "PC/pathway (problem-oriented) add" },
568
    { "PCC", "PC/pathway (problem-oriented) update" },
569
    { "PCD", "PC/pathway (problem-oriented) delete" },
570
    { "PCE", "PC/pathway (problem-oriented) query" },
571
    { "PCF", "PC/pathway (problem-oriented) query response" },
572
    { "PCG", "PC/pathway (goal-oriented) add" },
573
    { "PCH", "PC/pathway (goal-oriented) update" },
574
    { "PCJ", "PC/pathway (goal-oriented) delete" },
575
    { "PCK", "PC/pathway (goal-oriented) query" },
576
    { "PCL", "PC/pathway (goal-oriented) query response" },
577
    { "Q01", "Query sent for immediate response" },
578
    { "Q02", "Query sent for deferred response" },
579
    { "Q03", "Deferred response to a query" },
580
    { "Q05", "Unsolicited display update message" },
581
    { "Q06", "Query for order status" },
582
    { "Q11", "Query by parameter requesting an RSP segment pattern response" },
583
    { "Q13", "Query by parameter requesting an RTB tabular response" },
584
    { "Q15", "Query by parameter requesting an RDY display response" },
585
    { "Q16", "Create subscription" },
586
    { "Q17", "Query for previous events" },
587
    { "Q21", "Get person demographics" },
588
    { "Q22", "Find candidates" },
589
    { "Q23", "Get corresponding identifiers" },
590
    { "Q24", "Allocate identifiers" },
591
    { "Q25", "Personnel Information by Segment Query" },
592
    { "Q26", "Pharmacy/treatment order response" },
593
    { "Q27", "Pharmacy/treatment administration information" },
594
    { "Q28", "Pharmacy/treatment dispense information" },
595
    { "Q29", "Pharmacy/treatment encoded order information" },
596
    { "Q30", "Pharmacy/treatment dose information" },
597
    { "Q31", "Query Dispense history" },
598
    { "R01", "Unsolicited transmission of an observation message" },
599
    { "R02", "Query for results of observation" },
600
    { "R04", "Response to query; transmission of requested observation" },
601
    { "R21", "Unsolicited laboratory observation" },
602
    { "R22", "Unsolicited Specimen Oriented Observation Message" },
603
    { "R23", "Unsolicited Specimen Container Oriented Observation Message" },
604
    { "R24", "Unsolicited Order Oriented Observation Message" },
605
    { "R25", "Unsolicited Population/Location-Based Laboratory Observation Message" },
606
    { "R30", "Unsolicited Point-Of-Care Observation Message Without Existing Order - Place An Order" },
607
    { "R31", "Unsolicited New Point-Of-Care Observation Message - Search For An Order" },
608
    { "R32", "Unsolicited Pre-Ordered Point-Of-Care Observation" },
609
    { "ROR", "Pharmacy prescription order query response" },
610
    { "S01", "Request new appointment booking" },
611
    { "S02", "Request appointment rescheduling" },
612
    { "S03", "Request appointment modification" },
613
    { "S04", "Request appointment cancellation" },
614
    { "S05", "Request appointment discontinuation" },
615
    { "S06", "Request appointment deletion" },
616
    { "S07", "Request addition of service/resource on appointment" },
617
    { "S08", "Request modification of service/resource on appointment" },
618
    { "S09", "Request cancellation of service/resource on appointment" },
619
    { "S10", "Request discontinuation of service/resource on appointment" },
620
    { "S11", "Request deletion of service/resource on appointment" },
621
    { "S12", "Notification of new appointment booking" },
622
    { "S13", "Notification of appointment rescheduling" },
623
    { "S14", "Notification of appointment modification" },
624
    { "S15", "Notification of appointment cancellation" },
625
    { "S16", "Notification of appointment discontinuation" },
626
    { "S17", "Notification of appointment deletion" },
627
    { "S18", "Notification of addition of service/resource on appointment" },
628
    { "S19", "Notification of modification of service/resource on appointment" },
629
    { "S20", "Notification of cancellation of service/resource on appointment" },
630
    { "S21", "Notification of discontinuation of service/resource on appointment" },
631
    { "S22", "Notification of deletion of service/resource on appointment" },
632
    { "S23", "Notification of blocked schedule time slot(s)" },
633
    { "S24", "Notification of opened (\"unblocked\") schedule time slot(s)" },
634
    { "S25", "Schedule query message and response" },
635
    { "S26", "Notification that patient did not show up for schedule appointment" },
636
    { "S28", "Request new sterilization lot " },
637
    { "S29", "Request Sterilization lot deletion" },
638
    { "S30", "Request item" },
639
    { "S31", "Request anti-microbial device data" },
640
    { "S32", "Request anti-microbial device cycle data" },
641
    { "S33", "Notification of sterilization configuration" },
642
    { "S34", "Notification of sterilization lot" },
643
    { "S35", "Notification of sterilization lot deletion" },
644
    { "S36", "Notification of anti-microbial device data" },
645
    { "S37", "Notification of anti-microbial device cycle data" },
646
    { "T01", "Original document notification" },
647
    { "T02", "Original document notification and content" },
648
    { "T03", "Document status change notification" },
649
    { "T04", "Document status change notification and content" },
650
    { "T05", "Document addendum notification" },
651
    { "T06", "Document addendum notification and content" },
652
    { "T07", "Document edit notification" },
653
    { "T08", "Document edit notification and content" },
654
    { "T09", "Document replacement notification" },
655
    { "T10", "Document replacement notification and content" },
656
    { "T11", "Document cancel notification" },
657
    { "T12", "Document query" },
658
    { "U01", "Automated equipment status update" },
659
    { "U02", "Automated equipment status request" },
660
    { "U03", "Specimen status update" },
661
    { "U04", "specimen status request" },
662
    { "U05", "Automated equipment inventory update" },
663
    { "U06", "Automated equipment inventory request" },
664
    { "U07", "Automated equipment command" },
665
    { "U08", "Automated equipment response" },
666
    { "U09", "Automated equipment notification " },
667
    { "U10", "Automated equipment test code settings update" },
668
    { "U11", "Automated equipment test code settings request" },
669
    { "U12", "Automated equipment log/service update" },
670
    { "U13", "Automated equipment log/service request" },
671
    { "V01", "Query for vaccination record" },
672
    { "V02", "Response to vaccination query returning multiple PID matches" },
673
    { "V03", "Vaccination record response" },
674
    { "V04", "Unsolicited vaccination record update" },
675
    { "W01", "Waveform result, unsolicited transmission of requested information" },
676
    { "W02", "Waveform result, response to query " },
677
    { NULL, NULL }
678
};
679
680
static bool
681
0
event_present(const struct msh *msh) {
682
0
    return msh->trigger_event[0] == 0 ? false : true;
683
0
}
684
685
static int
686
parse_msh(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, unsigned offset,
687
          struct msh *msh)
688
0
{
689
0
    unsigned segment_len;
690
0
    unsigned end_of_segment_offset;
691
0
    unsigned field_separator_offset;
692
0
    unsigned field_number = 0;
693
694
    /* initialize msh */
695
0
    msh->trigger_event[0] ='\0';
696
0
    msh->message_type[0] = '\0';
697
698
    /* e.g. MSH|^~\&|||||||XZY^IJK|||||||\r */
699
0
    field_number = 1;
700
0
    offset += 3; // skip 'MSH'
701
0
    msh->field_separator = tvb_get_uint8(tvb, offset);
702
0
    offset += 1;
703
0
    msh->component_separator = tvb_get_uint8(tvb, offset);
704
0
    offset += 1;
705
0
    msh->repetition_separator = tvb_get_uint8(tvb, offset);
706
0
    offset += 1;
707
0
    msh->escape_character = tvb_get_uint8(tvb, offset);
708
0
    offset += 1;
709
0
    msh->subcomponent_separator = tvb_get_uint8(tvb, offset);
710
0
    offset += 1;
711
0
    field_number++;
712
713
    /* FF: even if HL7 2.3.1 says each segment must be terminated with CR
714
     * we look either for a CR or an LF or both (I did find a system out
715
     * there that uses both) */
716
0
    if (!tvb_find_line_end_remaining(tvb, offset, &segment_len, NULL)) {
717
0
        expert_add_info_format(pinfo, NULL, &ei_hl7_malformed,
718
0
                               "Segments must be terminated with CR");
719
0
        return -1;
720
0
    }
721
0
    end_of_segment_offset = offset + segment_len;
722
723
0
    while (offset < end_of_segment_offset) {
724
0
        if (!tvb_find_uint8_length(tvb, offset, end_of_segment_offset - offset, msh->field_separator, &field_separator_offset)) {
725
0
            if (field_number < 9) {
726
0
                expert_add_info_format(pinfo, NULL, &ei_hl7_malformed,
727
0
                                       "MSH must have at least 9 fields");
728
0
                return -1;
729
0
            }
730
0
            return 0;
731
0
        }
732
0
        field_number++;
733
0
        offset = field_separator_offset + 1;
734
0
        if (tvb_get_uint8(tvb, offset) == msh->field_separator) {
735
            /* skip the empty field '||' */
736
0
            continue;
737
0
        }
738
0
        if (field_number == 9) { /* 9th field is the message type[^event] */
739
0
            tvb_get_raw_bytes_as_string(tvb, offset, msh->message_type, 4);
740
0
            if (tree) {
741
0
                proto_item *hidden_item;
742
0
                hidden_item = proto_tree_add_item(tree, hf_hl7_message_type,
743
0
                                                  tvb, offset, 3,
744
0
                                                  ENC_ASCII);
745
0
                proto_item_set_hidden(hidden_item);
746
0
            }
747
0
            if (tvb_get_uint8(tvb, offset + 3) == msh->component_separator) {
748
0
                tvb_get_raw_bytes_as_string(tvb, offset + 4, msh->trigger_event, 4);
749
0
                if (tree) {
750
0
                    proto_item *hidden_item;
751
0
                    hidden_item = proto_tree_add_item(tree, hf_hl7_event_type,
752
0
                                                      tvb, offset + 4, 3,
753
0
                                                      ENC_ASCII);
754
0
                    proto_item_set_hidden(hidden_item);
755
0
                }
756
0
            }
757
0
        }
758
0
    }
759
0
    return 0;
760
0
}
761
762
static void
763
dissect_hl7_segment(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_,
764
                    unsigned offset, unsigned segment_len, unsigned segment_len_crlf _U_,
765
                    const struct msh *msh _U_)
766
0
{
767
    /* segment layout xyz|a|b||||c|d\rxyz|a|b|c||||d... */
768
0
    proto_tree *segment_tree = NULL;
769
0
    proto_item *ti = NULL;
770
0
    char *field_str = NULL;
771
0
    unsigned end_of_segment_offset = 0;
772
0
    unsigned field_separator_offset = 0;
773
0
    unsigned field_num = 0;
774
0
    unsigned field_len = 0;
775
0
    unsigned segment_consumed = 0;
776
0
    bool last_field = false;
777
778
    /* calculate where the segment ends */
779
0
    end_of_segment_offset = offset + segment_len;
780
781
    /* iterate over any fields */
782
0
    while (offset < end_of_segment_offset) {
783
784
0
        field_num++;
785
786
        /* get next '|' offset */
787
0
        if (!tvb_find_uint8_length(tvb, offset, segment_len - segment_consumed, msh->field_separator, &field_separator_offset)) {
788
            /* we do not have a field separator */
789
0
            if (segment_consumed != segment_len) {
790
                /* this is the last field */
791
0
                last_field = true;
792
0
                field_len = segment_len - segment_consumed;
793
0
                segment_consumed += field_len + 1;
794
0
            } else {
795
                /* end of tvb or reached maxlen (i.e. end of segment) */
796
0
                return;
797
0
            }
798
0
        } else {
799
            /* we have a field separator */
800
            /* calc field length and the amount of segment data consumed */
801
0
            field_len = field_separator_offset - offset;
802
0
            segment_consumed += field_len + 1;
803
0
        }
804
805
        /* skip empty fields '||' */
806
0
        if (field_len == 0) {
807
            /* move the offset after the separator, pointing to the next field */
808
0
            offset = field_separator_offset + 1;
809
0
            continue;
810
0
        }
811
812
        /* process the field (the 1st one generate a node in the tree view) */
813
0
        if (field_num == 1) {
814
0
            char *segment_type_id = NULL;
815
0
            segment_type_id = (char*)tvb_get_string_enc(pinfo->pool,
816
0
                                                 tvb, offset, 3, ENC_ASCII);
817
0
            ti = proto_tree_add_item(tree, hf_hl7_segment,
818
0
                                     tvb, offset, segment_len_crlf,
819
0
                                     ENC_ASCII);
820
0
            proto_item_set_text(ti, "%s (%s)", segment_type_id,
821
0
                                str_to_str_wmem(pinfo->pool, segment_type_id, hl7_seg_type_vals,
822
0
                                           "Unknown Segment"));
823
0
            segment_tree = proto_item_add_subtree(ti, ett_hl7_segment);
824
0
            if (global_hl7_raw) {
825
0
                proto_tree_add_item(segment_tree, hf_hl7_raw_segment, tvb, offset,
826
0
                                    segment_len_crlf, ENC_ASCII);
827
0
            }
828
0
        }
829
0
        field_str = (char*)tvb_get_string_enc(pinfo->pool,
830
0
                                       tvb, offset, field_len, ENC_ASCII);
831
0
        ti = proto_tree_add_item(segment_tree, hf_hl7_field,
832
0
                                 tvb, offset, field_len, ENC_ASCII);
833
0
        proto_item_set_text(ti, "field %d: %s", field_num, field_str);
834
835
        /* if this is the last field we are done */
836
0
        if (last_field) {
837
0
            return;
838
0
        }
839
840
        /* move the offset after the separator, pointing to the next field */
841
0
        offset = field_separator_offset + 1;
842
0
    }
843
0
}
844
845
static void
846
dissect_hl7_message(tvbuff_t *tvb, unsigned tvb_offset, unsigned len,
847
                    packet_info *pinfo, proto_tree *tree, void *data _U_)
848
0
{
849
0
    unsigned offset = tvb_offset;
850
0
    unsigned sob_offset = offset;
851
0
    unsigned eob_offset = offset + len - 2;
852
0
    proto_tree *hl7_tree = NULL;
853
0
    proto_item *ti = NULL;
854
0
    struct msh msh = {0};
855
0
    int ret = 0;
856
857
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "HL7");
858
0
    col_clear(pinfo->cinfo, COL_INFO);
859
860
0
    ret = parse_msh(tvb, pinfo, tree, offset + 1, &msh);
861
862
0
    if (ret == -1)
863
0
        return;
864
865
    /* enrich info column */
866
0
    if (event_present(&msh)) {
867
0
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s (%s)",
868
0
                            get_ascii_string(pinfo->pool, (uint8_t*)msh.message_type, 3),
869
0
                            get_ascii_string(pinfo->pool, (uint8_t*)msh.trigger_event, 3));
870
0
    } else {
871
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, NULL,
872
0
                           (char*)get_ascii_string(pinfo->pool, (uint8_t*)msh.message_type, 3));
873
0
    }
874
    /* set a fence so that subsequent col_clear calls will
875
     * not wipe out col information regarding this PDU */
876
0
    col_set_fence(pinfo->cinfo, COL_INFO);
877
878
0
    ti = proto_tree_add_item(tree, proto_hl7, tvb, offset, len, ENC_NA);
879
0
    if (event_present(&msh)) {
880
0
        proto_item_append_text(ti, ", Type: %s, Event: %s",
881
0
                               str_to_str_wmem(pinfo->pool, msh.message_type,
882
0
                                          hl7_msg_type_vals, "Unknown"),
883
0
                               str_to_str_wmem(pinfo->pool, msh.trigger_event,
884
0
                                          hl7_event_type_vals, "Unknown"));
885
0
    } else {
886
0
        proto_item_append_text(ti, ", Type: %s",
887
0
                               str_to_str_wmem(pinfo->pool, msh.message_type,
888
0
                                          hl7_msg_type_vals, "Unknown"));
889
0
    }
890
0
    hl7_tree = proto_item_add_subtree(ti, ett_hl7);
891
    /* SOB */
892
0
    if (global_hl7_llp) {
893
0
        proto_tree_add_item(hl7_tree, hf_hl7_llp_sob, tvb, sob_offset, 1, ENC_NA);
894
0
    }
895
0
    offset++;
896
0
    if (global_hl7_raw) {
897
0
        proto_tree_add_item(hl7_tree, hf_hl7_raw, tvb, offset, len - 3,
898
0
                            ENC_ASCII);
899
0
    }
900
901
    /* body */
902
0
    while (offset < eob_offset) {
903
0
        unsigned next_offset;
904
0
        unsigned segment_len;
905
0
        unsigned segment_len_crlf;
906
        /* FF: even if HL7 2.3.1 says each segment must be terminated with CR
907
         * we look either for a CR or an LF or both (I did find a system out
908
         * there that uses both) */
909
0
        if (!tvb_find_line_end_remaining(tvb, offset, &segment_len, &next_offset)) {
910
0
            expert_add_info_format(pinfo, NULL, &ei_hl7_malformed,
911
0
                                   "Segments must be terminated with CR");
912
0
            return;
913
0
        }
914
0
        segment_len_crlf = next_offset - offset;
915
0
        dissect_hl7_segment(tvb, pinfo, hl7_tree, offset, segment_len, segment_len_crlf, &msh);
916
0
        offset += segment_len_crlf;
917
0
    }
918
    /* EOB */
919
0
    if (global_hl7_llp) {
920
0
        proto_tree_add_item(hl7_tree, hf_hl7_llp_eob, tvb, eob_offset, 2,
921
0
                            ENC_BIG_ENDIAN);
922
0
    }
923
0
}
924
925
static int
926
dissect_hl7(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
927
6
{
928
6
    unsigned offset = 0;
929
930
6
    while (offset < tvb_reported_length(tvb)) {
931
6
        unsigned llp_eob_offset;
932
6
        if (!tvb_find_uint16_remaining(tvb, offset, LLP_EOB, &llp_eob_offset)) {
933
            /* we ran out of data: ask for more */
934
6
            pinfo->desegment_offset = offset;
935
6
            pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
936
6
            return tvb_reported_length(tvb);
937
6
        }
938
939
        /* tvb_find_ utilities return the *start* of the signature, here we
940
         * take care of the LLP_EOB bytes */
941
0
        unsigned llp_block_len = llp_eob_offset - offset + 2;
942
943
        /* FF: nasty case, check whether the capture started after the SOB
944
         * transmission. If this is the case we display these trailing bytes
945
         * as 'Data' and we will dissect the next complete message.
946
         */
947
0
        if (tvb_get_uint8(tvb, 0) != LLP_SOB) {
948
0
            tvbuff_t *new_tvb = tvb_new_subset_remaining(tvb, offset);
949
0
            call_data_dissector(new_tvb, pinfo, tree);
950
0
            return tvb_reported_length(tvb);
951
0
        }
952
953
        /* FF: ok we got a complete LLP block '0x0B HL7-message 0x1C 0x0D',
954
         * do the dissection */
955
0
        dissect_hl7_message(tvb, offset, llp_block_len, pinfo, tree, data);
956
0
        offset += llp_block_len;
957
0
    }
958
959
    /* if we get here, then the end of the tvb matched with the end of a
960
       HL7 message. Happy days. */
961
0
    return tvb_captured_length(tvb);
962
6
}
963
964
static bool
965
dissect_hl7_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
966
2.28k
{
967
2.28k
    conversation_t *conversation = NULL;
968
969
    /* heuristic is based on first 5 bytes analysis, we assume
970
       0x0B + "MSH|" is good enough */
971
2.28k
    if ((tvb_reported_length_remaining(tvb, 0) < 5) ||
972
2.15k
        (tvb_get_uint8(tvb, 0) != LLP_SOB) ||
973
2.28k
        (tvb_strncaseeql(tvb, 1, "MSH|", 4) != 0)) {
974
2.28k
        return false;
975
2.28k
    }
976
977
    /* heuristic test passed, associate the non-heuristic port based
978
     * dissector function above with this flow for further processing,
979
     * the conversation framework will do the rest */
980
0
    conversation = find_or_create_conversation(pinfo);
981
0
    conversation_set_dissector(conversation, hl7_handle);
982
983
    /* Note Well!
984
     * If this PDU is complete everything is fine, the engine will call
985
     * dissect_hl7() providing the same data we have in this tvb.
986
     * If the PDU is *not* complete - i.e. we have only the first
987
     * fragment in this tvb - then dissect_hl7() will get only the
988
     * next bytes, hence the first PDU will not be properly displayed.
989
     * To fix this case we need to tell the dissector engine that we
990
     * need more data (desegment_len = MORE) and that we want
991
     * to continue the next processing from the beginning of the PDU
992
     * (desegment_offset = 0) because we did not consume/dissect
993
     * anything in this cycle. */
994
995
0
    if (!tvb_find_uint16_remaining(tvb, 0, LLP_EOB, NULL)) {
996
0
        pinfo->desegment_offset = 0;
997
0
        pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
998
0
    }
999
1000
0
    return true;
1001
2.28k
}
1002
1003
void
1004
proto_reg_handoff_hl7(void)
1005
16
{
1006
    /* register as heuristic dissector for TCP */
1007
16
    heur_dissector_add("tcp", dissect_hl7_heur, "HL7 over TCP",
1008
16
                       "hl7_tcp", proto_hl7, HEURISTIC_ENABLE);
1009
1010
    /* register as normal dissector for TCP well-known port */
1011
16
    dissector_add_uint_with_preference("tcp.port", TCP_PORT_HL7, hl7_handle);
1012
16
}
1013
1014
void
1015
proto_register_hl7(void)
1016
16
{
1017
16
    static hf_register_info hl7f_info[] = {
1018
16
        { &hf_hl7_raw,
1019
16
          { "raw message", "hl7.raw", FT_STRING,
1020
16
            BASE_NONE, NULL, 0x0, NULL, HFILL }
1021
16
        },
1022
16
        { &hf_hl7_llp_sob,
1023
16
          { "LLP Start Of Block", "hl7.llp.sob", FT_UINT8,
1024
16
            BASE_HEX, NULL, 0x0, NULL, HFILL }
1025
16
        },
1026
16
        { &hf_hl7_llp_eob,
1027
16
          { "LLP End Of Block", "hl7.llp.eob", FT_UINT16,
1028
16
            BASE_HEX, NULL, 0x0, NULL, HFILL }
1029
16
        },
1030
16
        { &hf_hl7_raw_segment,
1031
16
          { "raw segment", "hl7.raw.segment", FT_STRING,
1032
16
            BASE_NONE, NULL, 0x0, NULL, HFILL }
1033
16
        },
1034
16
        { &hf_hl7_segment,
1035
16
          { "xyz", "hl7.segment", FT_STRING,
1036
16
            BASE_NONE, NULL, 0x0,
1037
16
            NULL, HFILL }
1038
16
        },
1039
16
        { &hf_hl7_message_type,
1040
16
          { "xyz", "hl7.message.type", FT_STRING,
1041
16
            BASE_NONE, NULL, 0x0,
1042
16
            NULL, HFILL }
1043
16
        },
1044
16
        { &hf_hl7_event_type,
1045
16
          { "xyz", "hl7.event.type", FT_STRING,
1046
16
            BASE_NONE, NULL, 0x0,
1047
16
            NULL, HFILL }
1048
16
        },
1049
16
        { &hf_hl7_field,
1050
16
          { "xyz", "hl7.field", FT_STRING,
1051
16
            BASE_NONE, NULL, 0x0,
1052
16
            NULL, HFILL }
1053
16
        },
1054
16
    };
1055
1056
16
    static int *ett[] = {
1057
16
        &ett_hl7,
1058
16
        &ett_hl7_segment,
1059
16
    };
1060
1061
16
    static ei_register_info ei[] = {
1062
16
        { &ei_hl7_malformed, { "hl7.malformed", PI_MALFORMED, PI_WARN, "Malformed", EXPFILL }},
1063
16
    };
1064
1065
16
    expert_module_t *expert_hl7 = NULL;
1066
16
    module_t *hl7_module = NULL;
1067
1068
16
    proto_hl7 = proto_register_protocol("Health Level Seven", "HL7", "hl7");
1069
16
    proto_register_field_array(proto_hl7, hl7f_info, array_length(hl7f_info));
1070
16
    proto_register_subtree_array(ett, array_length(ett));
1071
16
    expert_hl7 = expert_register_protocol(proto_hl7);
1072
16
    expert_register_field_array(expert_hl7, ei, array_length(ei));
1073
16
    hl7_module = prefs_register_protocol(proto_hl7, NULL);
1074
16
    prefs_register_bool_preference(hl7_module, "display_raw",
1075
16
                                   "Display raw text for HL7 message",
1076
16
                                   "Specifies that the raw text of the "
1077
16
                                   "HL7 message should be displayed "
1078
16
                                   "in addition to the dissection tree",
1079
16
                                   &global_hl7_raw);
1080
16
    prefs_register_bool_preference(hl7_module, "display_llp",
1081
16
                                   "Display LLP markers (Start/End Of Block)",
1082
16
                                   "Specifies that the LLP session information "
1083
16
                                   "should be displayed (Start/End Of Block) "
1084
16
                                   "in addition to the dissection tree",
1085
16
                                   &global_hl7_llp);
1086
1087
16
    hl7_handle = register_dissector("hl7", dissect_hl7, proto_hl7);
1088
16
}
1089
1090
/*
1091
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1092
 *
1093
 * Local variables:
1094
 * c-basic-offset: 4
1095
 * tab-width: 8
1096
 * indent-tabs-mode: nil
1097
 * End:
1098
 *
1099
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1100
 * :indentSize=4:tabSize=8:noTabs=true:
1101
 */