/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 | | */ |