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.
5  *
6  * Copyright 2016 Francesco Fondelli <francesco dot fondelli, gmail dot com>
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * SPDX-License-Identifier: GPL-2.0-or-later
13  *
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.
25  */
26 
27 #include "config.h"
28 
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 
35 #include <stdio.h>
36 
37 void proto_register_hl7(void);
38 void proto_reg_handoff_hl7(void);
39 
40 /* 2575 is registered at IANA for HL7 */
41 #define TCP_PORT_HL7 2575
42 #define LLP_SOB 0x0B /* Start Of Block byte */
43 #define LLP_EOB 0x1C0D /* End Of Block byte + \r */
44 
45 struct msh {                    // typical/default values
46     char field_separator;       // '|'
47     char component_separator;   // '^'
48     char repetition_separator;  // '~'
49     char escape_character;      // '\'
50     char subcomponent_separator;// '&'
51     char message_type[4];
52     char trigger_event[4];
53 };
54 
55 dissector_handle_t hl7_handle;
56 dissector_handle_t hl7_heur_handle;
57 
58 static int proto_hl7 = -1;
59 
60 static gint hf_hl7_raw = -1;
61 static gint hf_hl7_raw_segment = -1;
62 static gint hf_hl7_llp_sob = -1;
63 static gint hf_hl7_llp_eob = -1;
64 static gint hf_hl7_message_type = -1;
65 static gint hf_hl7_event_type = -1;
66 static gint hf_hl7_segment = -1;
67 static gint hf_hl7_field = -1;
68 
69 static gint ett_hl7 = -1;
70 static gint ett_hl7_segment = -1;
71 
72 static expert_field ei_hl7_malformed = EI_INIT;
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 gboolean global_hl7_raw = FALSE;
77 
78 /* FF: global_hl7_llp determines whether we are going to display
79  * the LLP block markers */
80 static gboolean global_hl7_llp = FALSE;
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 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 gboolean
event_present(const struct msh * msh)681 event_present(const struct msh *msh) {
682     return msh->trigger_event[0] == 0 ? FALSE : TRUE;
683 }
684 
685 static int
parse_msh(tvbuff_t * tvb,packet_info * pinfo _U_,proto_tree * tree,gint offset,struct msh * msh)686 parse_msh(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, gint offset,
687           struct msh *msh)
688 {
689     gint segment_len = -1;
690     gint end_of_segment_offset = -1;
691     gint field_separator_offset = -1;
692     gint field_number = 0;
693 
694     /* initialize msh */
695     msh->trigger_event[0] ='\0';
696     msh->message_type[0] = '\0';
697 
698     /* e.g. MSH|^~\&|||||||XZY^IJK|||||||\r */
699     field_number = 1;
700     offset += 3; // skip 'MSH'
701     msh->field_separator = tvb_get_guint8(tvb, offset);
702     offset += 1;
703     msh->component_separator = tvb_get_guint8(tvb, offset);
704     offset += 1;
705     msh->repetition_separator = tvb_get_guint8(tvb, offset);
706     offset += 1;
707     msh->escape_character = tvb_get_guint8(tvb, offset);
708     offset += 1;
709     msh->subcomponent_separator = tvb_get_guint8(tvb, offset);
710     offset += 1;
711     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     segment_len = tvb_find_line_end(tvb, offset, -1, NULL, TRUE);
717     if (segment_len == -1) {
718         expert_add_info_format(pinfo, NULL, &ei_hl7_malformed,
719                                "Segments must be terminated with CR");
720         return -1;
721     }
722     end_of_segment_offset = offset + segment_len;
723 
724     while (offset < end_of_segment_offset) {
725         field_separator_offset =
726             tvb_find_guint8(tvb, offset, end_of_segment_offset - offset,
727                             msh->field_separator);
728         if (field_separator_offset == -1) {
729             if (field_number < 9) {
730                 expert_add_info_format(pinfo, NULL, &ei_hl7_malformed,
731                                        "MSH must have at least 9 fields");
732                 return -1;
733             }
734             return 0;
735         }
736         field_number++;
737         offset = field_separator_offset + 1;
738         if (tvb_get_guint8(tvb, offset) == msh->field_separator) {
739             /* skip the empty field '||' */
740             continue;
741         }
742         if (field_number == 9) { /* 9th field is the message type[^event] */
743             msh->message_type[0] = tvb_get_guint8(tvb, offset);
744             msh->message_type[1] = tvb_get_guint8(tvb, offset + 1);
745             msh->message_type[2] = tvb_get_guint8(tvb, offset + 2);
746             msh->message_type[3] = '\0';
747             if (tree) {
748                 proto_item *hidden_item;
749                 hidden_item = proto_tree_add_item(tree, hf_hl7_message_type,
750                                                   tvb, offset, 3,
751                                                   ENC_ASCII|ENC_NA);
752                 proto_item_set_hidden(hidden_item);
753             }
754             if (tvb_get_guint8(tvb, offset + 3) == msh->component_separator) {
755                 msh->trigger_event[0] = tvb_get_guint8(tvb, offset + 4);
756                 msh->trigger_event[1] = tvb_get_guint8(tvb, offset + 5);
757                 msh->trigger_event[2] = tvb_get_guint8(tvb, offset + 6);
758                 msh->trigger_event[3] = '\0';
759                 if (tree) {
760                     proto_item *hidden_item;
761                     hidden_item = proto_tree_add_item(tree, hf_hl7_event_type,
762                                                       tvb, offset + 4, 3,
763                                                       ENC_ASCII|ENC_NA);
764                     proto_item_set_hidden(hidden_item);
765                 }
766             }
767         }
768     }
769     return 0;
770 }
771 
772 static void
dissect_hl7_segment(tvbuff_t * tvb,packet_info * pinfo _U_,proto_tree * tree _U_,gint offset,gint segment_len,gint segment_len_crlf _U_,const struct msh * msh _U_)773 dissect_hl7_segment(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_,
774                     gint offset, gint segment_len, gint segment_len_crlf _U_,
775                     const struct msh *msh _U_)
776 {
777     /* segment layout xyz|a|b||||c|d\rxyz|a|b|c||||d... */
778     proto_tree *segment_tree = NULL;
779     proto_item *ti = NULL;
780     char *field_str = NULL;
781     gint end_of_segment_offset = 0;
782     gint field_separator_offset = 0;
783     gint field_num = 0;
784     gint field_len = 0;
785     gint segment_consumed = 0;
786     gboolean last_field = FALSE;
787 
788     /* calculate where the segment ends */
789     end_of_segment_offset = offset + segment_len;
790 
791     /* iterate over any fields */
792     while (offset < end_of_segment_offset) {
793 
794         field_num++;
795 
796         /* get next '|' offset */
797         field_separator_offset =
798             tvb_find_guint8(tvb, offset,
799                             segment_len - segment_consumed,
800                             msh->field_separator);
801 
802         if (field_separator_offset == -1) {
803             /* we do not have a field separator */
804             if (segment_consumed != segment_len) {
805                 /* this is the last field */
806                 last_field = TRUE;
807                 field_len = segment_len - segment_consumed;
808                 segment_consumed += field_len + 1;
809             } else {
810                 /* end of tvb or reached maxlen (i.e. end of segment) */
811                 return;
812             }
813         } else {
814             /* we have a field separator */
815             /* calc field length and the amount of segment data consumed */
816             field_len = field_separator_offset - offset;
817             segment_consumed += field_len + 1;
818         }
819 
820         /* skip empty fields '||' */
821         if (field_len == 0) {
822             /* move the offset after the separator, pointing to the next field */
823             offset = field_separator_offset + 1;
824             continue;
825         }
826 
827         /* process the field (the 1st one generate a node in the tree view) */
828         if (field_num == 1) {
829             char *segment_type_id = NULL;
830             segment_type_id = tvb_get_string_enc(pinfo->pool,
831                                                  tvb, offset, 3, ENC_ASCII);
832             ti = proto_tree_add_item(tree, hf_hl7_segment,
833                                      tvb, offset, segment_len_crlf,
834                                      ENC_ASCII|ENC_NA);
835             proto_item_set_text(ti, "%s (%s)", segment_type_id,
836                                 str_to_str(segment_type_id, hl7_seg_type_vals,
837                                            "Unknown Segment"));
838             segment_tree = proto_item_add_subtree(ti, ett_hl7_segment);
839             if (global_hl7_raw) {
840                 proto_tree_add_item(segment_tree, hf_hl7_raw_segment, tvb, offset,
841                                     segment_len_crlf, ENC_ASCII|ENC_NA);
842             }
843         }
844         field_str = tvb_get_string_enc(pinfo->pool,
845                                        tvb, offset, field_len, ENC_ASCII);
846         ti = proto_tree_add_item(segment_tree, hf_hl7_field,
847                                  tvb, offset, field_len, ENC_ASCII|ENC_NA);
848         proto_item_set_text(ti, "field %d: %s", field_num, field_str);
849 
850         /* if this is the last field we are done */
851         if (last_field) {
852             return;
853         }
854 
855         /* move the offset after the separator, pointing to the next field */
856         offset = field_separator_offset + 1;
857     }
858 }
859 
860 static void
dissect_hl7_message(tvbuff_t * tvb,guint tvb_offset,gint len,packet_info * pinfo,proto_tree * tree,void * data _U_)861 dissect_hl7_message(tvbuff_t *tvb, guint tvb_offset, gint len,
862                     packet_info *pinfo, proto_tree *tree, void *data _U_)
863 {
864     guint offset = tvb_offset;
865     guint sob_offset = offset;
866     guint eob_offset = offset + len - 2;
867     proto_tree *hl7_tree = NULL;
868     proto_item *ti = NULL;
869     struct msh msh;
870     int ret = 0;
871 
872     col_set_str(pinfo->cinfo, COL_PROTOCOL, "HL7");
873     col_clear(pinfo->cinfo, COL_INFO);
874 
875     ret = parse_msh(tvb, pinfo, tree, offset + 1, &msh);
876 
877     if (ret == -1)
878         return;
879 
880     /* enrich info column */
881     if (event_present(&msh)) {
882         if (offset == 0) {
883             col_append_fstr(pinfo->cinfo, COL_INFO, "%s (%s)",
884                             msh.message_type,
885                             msh.trigger_event);
886         } else {
887             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s (%s)",
888                             msh.message_type,
889                             msh.trigger_event);
890         }
891     } else {
892         if (offset == 0) {
893             col_append_str(pinfo->cinfo, COL_INFO,
894                             msh.message_type);
895         } else {
896             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
897                             msh.message_type);
898         }
899     }
900     /* set a fence so that subsequent col_clear calls will
901      * not wipe out col information regarding this PDU */
902     col_set_fence(pinfo->cinfo, COL_INFO);
903 
904     ti = proto_tree_add_item(tree, proto_hl7, tvb, offset, len, ENC_NA);
905     if (event_present(&msh)) {
906         proto_item_append_text(ti, ", Type: %s, Event: %s",
907                                str_to_str(msh.message_type,
908                                           hl7_msg_type_vals, "Unknown"),
909                                str_to_str(msh.trigger_event,
910                                           hl7_event_type_vals, "Unknown"));
911     } else {
912         proto_item_append_text(ti, ", Type: %s",
913                                str_to_str(msh.message_type,
914                                           hl7_msg_type_vals, "Unknown"));
915     }
916     hl7_tree = proto_item_add_subtree(ti, ett_hl7);
917     /* SOB */
918     if (global_hl7_llp) {
919         proto_tree_add_item(hl7_tree, hf_hl7_llp_sob, tvb, sob_offset, 1, ENC_NA);
920     }
921     offset++;
922     if (global_hl7_raw) {
923         proto_tree_add_item(hl7_tree, hf_hl7_raw, tvb, offset, len - 3,
924                             ENC_ASCII|ENC_NA);
925     }
926 
927     /* body */
928     while (offset < eob_offset) {
929         gint next_offset = -1;
930         gint segment_len = -1;
931         gint segment_len_crlf = -1;
932         /* FF: even if HL7 2.3.1 says each segment must be terminated with CR
933          * we look either for a CR or an LF or both (I did find a system out
934          * there that uses both) */
935         segment_len = tvb_find_line_end(tvb, offset, -1, &next_offset, TRUE);
936         if (segment_len == -1) {
937             expert_add_info_format(pinfo, NULL, &ei_hl7_malformed,
938                                    "Segments must be terminated with CR");
939             return;
940         }
941         segment_len_crlf = next_offset - offset;
942         dissect_hl7_segment(tvb, pinfo, hl7_tree,
943                             offset, segment_len, segment_len_crlf, &msh);
944         offset += segment_len_crlf;
945     }
946     /* EOB */
947     if (global_hl7_llp) {
948         proto_tree_add_item(hl7_tree, hf_hl7_llp_eob, tvb, eob_offset, 2,
949                             ENC_BIG_ENDIAN);
950     }
951 }
952 
953 static int
dissect_hl7(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)954 dissect_hl7(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
955 {
956     guint offset = 0;
957 
958     while (offset < tvb_reported_length(tvb)) {
959         gint available = tvb_reported_length_remaining(tvb, offset);
960         gint llp_eob_offset = tvb_find_guint16(tvb, offset, offset + available, LLP_EOB);
961 
962         if (llp_eob_offset == -1) {
963             /* we ran out of data: ask for more */
964             pinfo->desegment_offset = offset;
965             pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
966             return (offset + available);
967         }
968 
969         /* tvb_find_ utilities return the *start* of the signature, here we
970          * take care of the LLP_EOB bytes */
971         gint llp_block_len = llp_eob_offset - offset + 2;
972 
973         /* FF: nasty case, check whether the capture started after the SOB
974          * transmission. If this is the case we display these trailing bytes
975          * as 'Data' and we will dissect the next complete message.
976          */
977         if (tvb_get_guint8(tvb, 0) != LLP_SOB) {
978             tvbuff_t *new_tvb = tvb_new_subset_remaining(tvb, offset);
979             call_data_dissector(new_tvb, pinfo, tree);
980             return (offset + available);
981         }
982 
983         /* FF: ok we got a complete LLP block '0x0B HL7-message 0x1C 0x0D',
984          * do the dissection */
985         dissect_hl7_message(tvb, offset, llp_block_len, pinfo, tree, data);
986         offset += (guint)llp_block_len;
987     }
988 
989     /* if we get here, then the end of the tvb matched with the end of a
990        HL7 message. Happy days. */
991     return tvb_captured_length(tvb);
992 }
993 
994 static gboolean
dissect_hl7_heur(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree _U_,void * data _U_)995 dissect_hl7_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
996 {
997     conversation_t *conversation = NULL;
998 
999     /* heuristic is based on first 5 bytes analisys, we assume
1000        0x0B + "MSH|" is good enough */
1001     if ((tvb_reported_length_remaining(tvb, 0) < 5) ||
1002         (tvb_get_guint8(tvb, 0) != LLP_SOB) ||
1003         (tvb_strncaseeql(tvb, 1, "MSH|", 4) != 0)) {
1004         return FALSE;
1005     }
1006 
1007     /* heuristic test passed, associate the non-heuristic port based
1008      * dissector function above with this flow for further processing,
1009      * the conversation framework will do the rest */
1010     conversation = find_or_create_conversation(pinfo);
1011     conversation_set_dissector(conversation, hl7_handle);
1012 
1013     /* Note Well!
1014      * If this PDU is complete everything is fine, the engine will call
1015      * dissect_hl7() providing the same data we have in this tvb.
1016      * If the PDU is *not* complete - i.e. we have only the first
1017      * fragment in this tvb - then dissect_hl7() will get only the
1018      * next bytes, hence the first PDU will not be properly displayed.
1019      * To fix this case we need to tell the dissector engine that we
1020      * need more data (desegment_len = MORE) and that we want
1021      * to continue the next processing from the beginning of the PDU
1022      * (desegment_offset = 0) because we did not consume/dissect
1023      * anything in this cycle. */
1024     gint llp_eob_offset = tvb_find_guint16(tvb, 0, -1, LLP_EOB);
1025 
1026     if (llp_eob_offset == -1) {
1027         pinfo->desegment_offset = 0;
1028         pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
1029     }
1030 
1031     return TRUE;
1032 }
1033 
1034 void
proto_reg_handoff_hl7(void)1035 proto_reg_handoff_hl7(void)
1036 {
1037     /* register as heuristic dissector for TCP */
1038     hl7_heur_handle = create_dissector_handle(dissect_hl7_heur, proto_hl7);
1039     heur_dissector_add("tcp", dissect_hl7_heur, "HL7 over TCP",
1040                        "hl7_tcp", proto_hl7, HEURISTIC_ENABLE);
1041 
1042     /* register as normal dissector for TCP well-known port */
1043     hl7_handle = create_dissector_handle(dissect_hl7, proto_hl7);
1044     dissector_add_uint_with_preference("tcp.port", TCP_PORT_HL7, hl7_handle);
1045 }
1046 
1047 void
proto_register_hl7(void)1048 proto_register_hl7(void)
1049 {
1050     static hf_register_info hl7f_info[] = {
1051         { &hf_hl7_raw,
1052           { "raw message", "hl7.raw", FT_STRING,
1053             BASE_NONE, NULL, 0x0, NULL, HFILL }
1054         },
1055         { &hf_hl7_llp_sob,
1056           { "LLP Start Of Block", "hl7.llp.sob", FT_UINT8,
1057             BASE_HEX, NULL, 0x0, NULL, HFILL }
1058         },
1059         { &hf_hl7_llp_eob,
1060           { "LLP End Of Block", "hl7.llp.eob", FT_UINT16,
1061             BASE_HEX, NULL, 0x0, NULL, HFILL }
1062         },
1063         { &hf_hl7_raw_segment,
1064           { "raw segment", "hl7.raw.segment", FT_STRING,
1065             BASE_NONE, NULL, 0x0, NULL, HFILL }
1066         },
1067         { &hf_hl7_segment,
1068           { "xyz", "hl7.segment", FT_STRING,
1069             BASE_NONE, NULL, 0x0,
1070             NULL, HFILL }
1071         },
1072         { &hf_hl7_message_type,
1073           { "xyz", "hl7.message.type", FT_STRING,
1074             BASE_NONE, NULL, 0x0,
1075             NULL, HFILL }
1076         },
1077         { &hf_hl7_event_type,
1078           { "xyz", "hl7.event.type", FT_STRING,
1079             BASE_NONE, NULL, 0x0,
1080             NULL, HFILL }
1081         },
1082         { &hf_hl7_field,
1083           { "xyz", "hl7.field", FT_STRING,
1084             BASE_NONE, NULL, 0x0,
1085             NULL, HFILL }
1086         },
1087     };
1088 
1089     static gint *ett[] = {
1090         &ett_hl7,
1091         &ett_hl7_segment,
1092     };
1093 
1094     static ei_register_info ei[] = {
1095         { &ei_hl7_malformed, { "hl7.malformed", PI_MALFORMED, PI_WARN, "Malformed", EXPFILL }},
1096     };
1097 
1098     expert_module_t *expert_hl7 = NULL;
1099     module_t *hl7_module = NULL;
1100 
1101     proto_hl7 = proto_register_protocol("Health Level Seven", "HL7", "hl7");
1102     proto_register_field_array(proto_hl7, hl7f_info, array_length(hl7f_info));
1103     proto_register_subtree_array(ett, array_length(ett));
1104     expert_hl7 = expert_register_protocol(proto_hl7);
1105     expert_register_field_array(expert_hl7, ei, array_length(ei));
1106     hl7_module = prefs_register_protocol(proto_hl7, NULL);
1107     prefs_register_bool_preference(hl7_module, "display_raw",
1108                                    "Display raw text for HL7 message",
1109                                    "Specifies that the raw text of the "
1110                                    "HL7 message should be displayed "
1111                                    "in addition to the dissection tree",
1112                                    &global_hl7_raw);
1113     prefs_register_bool_preference(hl7_module, "display_llp",
1114                                    "Display LLP markers (Start/End Of Block)",
1115                                    "Specifies that the LLP session information "
1116                                    "should be displayed (Start/End Of Block) "
1117                                    "in addition to the dissection tree",
1118                                    &global_hl7_llp);
1119 }
1120 
1121 /*
1122  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1123  *
1124  * Local variables:
1125  * c-basic-offset: 4
1126  * tab-width: 8
1127  * indent-tabs-mode: nil
1128  * End:
1129  *
1130  * vi: set shiftwidth=4 tabstop=8 expandtab:
1131  * :indentSize=4:tabSize=8:noTabs=true:
1132  */
1133