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