2 * Routines for SAP IGS (Internet Graphics Server) dissection
3 * Copyright 2022, Yvan Genuer (@iggy38), Devoteam
4 * Copyright 2022, Martin Gallo <martin.gallo [AT] gmail.com>
5 * Code contributed by SecureAuth Corp.
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
15 * This is a simple dissector for the IGS protocol, initially added by Yvan Genuer as part of their research around the protocol:
16 * https://www.troopers.de/troopers18/agenda/3r38lr/. Some details and example requests can be found in pysap's documentation:
17 * https://pysap.readthedocs.io/en/latest/protocols/SAPIGS.html
24 #include <epan/packet.h>
25 #include <epan/prefs.h>
26 #include <epan/expert.h>
27 #include <wsutil/strtoi.h>
28 #include <wsutil/wmem/wmem.h>
31 * Define default ports. The right range should be 4NNNN, but as port numbers are proprietary and not
32 * IANA assigned, we leave only the ones corresponding to the instance 00.
34 #define SAPIGS_PORT_RANGE "40000"
36 /* IGS Functions values */
37 static const value_string sapigs_function_lst
[] = {
38 { 1, "ADM:REGPW"}, /* Register a PortWatcher */
39 { 2, "ADM:UNREGPW"}, /* Unregister a PortWatcher */
40 { 3, "ADM:REGIP"}, /* Register an Interpreter */
41 { 4, "ADM:UNREGIP"}, /* Unregister an Interpreter */
42 { 5, "ADM:FREEIP"}, /* Inform than Interpreter is free */
43 { 6, "ADM:ILLBEBACK"}, /* Call back function */
44 { 7, "ADM:ABORT"}, /* Abort Interpreter work */
45 { 8, "ADM:PING"}, /* Ping receive */
46 { 9, "ADM:PONG"}, /* Ping send */
47 { 10, "ADM:SHUTDOWNIGS"}, /* Shutdown IGS */
48 { 11, "ADM:SHUTDOWNPW"}, /* Shutdown PortWatcher */
49 { 12, "ADM:CHECKCONSUMER"}, /* Check Portwatcher status */
50 { 13, "ADM:FREECONSUMER"}, /* Inform than portwather is free */
51 { 14, "ADM:GETLOGFILE"}, /* Display log file */
52 { 15, "ADM:GETCONFIGFILE"}, /* Display configfile */
53 { 16, "ADM:GETDUMP"}, /* Display dump file */
54 { 17, "ADM:DELETEDUMP"}, /* Delete dump file */
55 { 18, "ADM:INSTALL"}, /* Upload shapefiles for GIS */
56 { 19, "ADM:SWITCH"}, /* Switch trace log level */
57 { 20, "ADM:GETVERSION"}, /* Get IGS Version */
58 { 21, "ADM:STATUS"}, /* Display IGS Status */
59 { 22, "ADM:STATISTIC"}, /* old Display IGS Statistic */
60 { 23, "ADM:STATISTICNEW"}, /* Display IGS Statistic */
61 { 24, "ADM:GETSTATCHART"}, /* Get IGS Statistic chart */
62 { 25, "ADM:SIM"}, /* Simulation function */
63 { 30, "ZIPPER"}, /* ZIP provide file(s) */
64 { 31, "IMGCONV"}, /* Image converter */
65 { 32, "RSPOCONNECTOR"}, /* Remote Spool Connector */
66 { 33, "XMLCHART"}, /* Chart generator through xml input */
67 { 34, "CHART"}, /* Chart generator through ABAP Table input */
68 { 35, "BWGIS"}, /* BW Geographic Information System */
69 { 36, "SAPGISXML"}, /* old SAP GIS through xml input */
74 static int proto_sapigs
;
77 static int hf_sapigs_function
;
78 static int hf_sapigs_listener
;
79 static int hf_sapigs_hostname
;
80 static int hf_sapigs_id
;
81 static int hf_sapigs_padd1
;
82 static int hf_sapigs_flag1
;
83 static int hf_sapigs_padd2
;
84 static int hf_sapigs_flag2
;
85 static int hf_sapigs_padd3
;
88 static int hf_sapigs_eye_catcher
;
89 static int hf_sapigs_padd4
;
90 static int hf_sapigs_codepage
;
91 static int hf_sapigs_offset_data
;
92 static int hf_sapigs_data_size
;
93 static int hf_sapigs_data
;
95 /* Table definition */
96 static int hf_sapigs_tables
;
97 static int hf_sapigs_table_version
;
98 static int hf_sapigs_table_name
;
99 static int hf_sapigs_table_line_number
;
100 static int hf_sapigs_table_width
;
101 static int hf_sapigs_table_column_name
;
102 static int hf_sapigs_table_column_number
;
103 static int hf_sapigs_table_column_width
;
106 static int hf_sapigs_portwatcher
;
107 static int hf_sapigs_portwatcher_version
;
108 static int hf_sapigs_portwatcher_info
;
109 static int hf_sapigs_interpreter
;
110 static int hf_sapigs_chart_config
;
112 static int ett_sapigs
;
114 /* Global port preference */
115 static range_t
*global_sapigs_port_range
;
117 /* Global highlight preference */
118 static bool global_sapigs_highlight_items
= true;
120 /* Protocol handle */
121 static dissector_handle_t sapigs_handle
;
123 void proto_reg_handoff_sapigs(void);
124 void proto_register_sapigs(void);
128 dissect_sapigs(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
130 uint32_t offset
= 0, err_val
= 0;
131 int data_offset
= 0, data_length
= 0;
132 char *sapigs_info_function
= NULL
, *illbeback_type
= NULL
, *is_table
= NULL
;
133 proto_item
*ti
= NULL
, *sapigs_tables
= NULL
;
134 proto_tree
*sapigs_tree
= NULL
, *sapigs_tables_tree
= NULL
;
136 /* Add the protocol to the column */
137 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "SAPIGS");
138 /* Add function name in the info column */
139 col_add_fstr(pinfo
->cinfo
, COL_INFO
, " function: %s", tvb_get_string_enc(pinfo
->pool
, tvb
, 0, 32, ENC_ASCII
));
141 /* Add the main sapigs subtree */
142 ti
= proto_tree_add_item(tree
, proto_sapigs
, tvb
, 0, -1, ENC_NA
);
143 sapigs_tree
= proto_item_add_subtree(ti
, ett_sapigs
);
145 /* Retrieve function name */
146 sapigs_info_function
= (char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 32, ENC_ASCII
);
149 proto_tree_add_item(sapigs_tree
, hf_sapigs_function
, tvb
, offset
, 32, ENC_ASCII
|ENC_NA
);
151 proto_tree_add_item(sapigs_tree
, hf_sapigs_listener
, tvb
, offset
, 32, ENC_ASCII
|ENC_NA
);
153 proto_tree_add_item(sapigs_tree
, hf_sapigs_hostname
, tvb
, offset
, 81, ENC_ASCII
|ENC_NA
);
155 proto_tree_add_item(sapigs_tree
, hf_sapigs_id
, tvb
, offset
, 4, ENC_ASCII
|ENC_NA
);
157 proto_tree_add_item(sapigs_tree
, hf_sapigs_padd1
, tvb
, offset
, 15, ENC_ASCII
);
159 proto_tree_add_item(sapigs_tree
, hf_sapigs_flag1
, tvb
, offset
, 1, ENC_ASCII
);
161 proto_tree_add_item(sapigs_tree
, hf_sapigs_padd2
, tvb
, offset
, 20, ENC_ASCII
);
163 proto_tree_add_item(sapigs_tree
, hf_sapigs_flag2
, tvb
, offset
, 1, ENC_ASCII
);
165 proto_tree_add_item(sapigs_tree
, hf_sapigs_padd3
, tvb
, offset
, 6, ENC_ASCII
);
168 /* switch over function name value */
169 switch (str_to_val(sapigs_info_function
, sapigs_function_lst
, err_val
)){
170 case 8:{ /* ADM:PING */
171 proto_tree_add_item(sapigs_tree
, hf_sapigs_portwatcher
, tvb
, offset
, 5, ENC_ASCII
|ENC_NA
);
174 case 9:{ /* ADM:PONG */
177 case 1:{ /* ADM:REGPW */
178 proto_tree_add_item(sapigs_tree
, hf_sapigs_portwatcher
, tvb
, offset
, 5, ENC_ASCII
|ENC_NA
);
180 proto_tree_add_item(sapigs_tree
, hf_sapigs_portwatcher_version
, tvb
, offset
, 16, ENC_ASCII
|ENC_NA
);
183 case 3: /* ADM:REGIP */
184 case 5:{ /* ADM:FREEIP */
185 proto_tree_add_item(sapigs_tree
, hf_sapigs_portwatcher
, tvb
, offset
, 5, ENC_ASCII
|ENC_NA
);
187 proto_tree_add_item(sapigs_tree
, hf_sapigs_interpreter
, tvb
, offset
, 16, ENC_ASCII
|ENC_NA
);
189 proto_tree_add_item(sapigs_tree
, hf_sapigs_portwatcher_version
, tvb
, offset
, 16, ENC_ASCII
|ENC_NA
);
191 proto_tree_add_item(sapigs_tree
, hf_sapigs_portwatcher_info
, tvb
, offset
, 16, ENC_ASCII
|ENC_NA
);
194 case 6:{ /* ADM:ILLBEBACK */
195 illbeback_type
= (char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 10, ENC_ASCII
|ENC_NA
);
196 if (strncmp("TransMagic", illbeback_type
, 10) == 0){
197 /* data is raw after eye_catcher */
198 proto_tree_add_item(sapigs_tree
, hf_sapigs_eye_catcher
, tvb
, offset
, 10, ENC_ASCII
|ENC_NA
);
200 proto_tree_add_item(sapigs_tree
, hf_sapigs_data
, tvb
, offset
, -1, ENC_NA
);
202 /* we receive sized data */
203 ws_strtoi((char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 5, ENC_ASCII
), NULL
, &data_length
);
204 proto_tree_add_item(sapigs_tree
, hf_sapigs_data_size
, tvb
, offset
, 5, ENC_ASCII
|ENC_NA
);
207 if ((data_length
> 0) && (tvb_reported_length_remaining(tvb
, offset
) >= data_length
)) {
208 proto_tree_add_item(sapigs_tree
, hf_sapigs_data
, tvb
, offset
, data_length
, ENC_NA
);
213 case 30: /* ZIPPER */
214 case 31: /* IMGCONV */
215 case 33: /* XMLCHART */
216 case 16:{ /* ADM:GETDUMP */
217 proto_tree_add_item(sapigs_tree
, hf_sapigs_eye_catcher
, tvb
, offset
, 10, ENC_ASCII
|ENC_NA
);
219 proto_tree_add_item(sapigs_tree
, hf_sapigs_padd4
, tvb
, offset
, 2, ENC_ASCII
);
221 proto_tree_add_item(sapigs_tree
, hf_sapigs_codepage
, tvb
, offset
, 4, ENC_ASCII
|ENC_NA
);
224 ws_strtoi((char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 16, ENC_ASCII
), NULL
, &data_offset
);
225 proto_tree_add_item(sapigs_tree
, hf_sapigs_offset_data
, tvb
, offset
, 16, ENC_ASCII
|ENC_NA
);
228 ws_strtoi((char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 16, ENC_ASCII
), NULL
, &data_length
);
229 proto_tree_add_item(sapigs_tree
, hf_sapigs_data_size
, tvb
, offset
, 16, ENC_ASCII
|ENC_NA
);
231 data_offset
+= offset
;
232 /* Definition tables */
233 is_table
= (char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 4, ENC_ASCII
);
234 /* if the 4 next char is VERS, we are at the beginning of one definition table */
235 while(strncmp("VERS", is_table
, 4) == 0){
236 /* Build a tree for Tables */
237 sapigs_tables
= proto_tree_add_item(sapigs_tree
, hf_sapigs_tables
, tvb
, offset
, 336, ENC_NA
);
238 sapigs_tables_tree
= proto_item_add_subtree(sapigs_tables
, ett_sapigs
);
239 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_version
, tvb
, offset
+8, 40, ENC_ASCII
);
241 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_name
, tvb
, offset
+8, 40, ENC_ASCII
);
243 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_line_number
, tvb
, offset
+8, 40, ENC_ASCII
);
245 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_width
, tvb
, offset
+8, 40, ENC_ASCII
);
247 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_column_name
, tvb
, offset
+8, 40, ENC_ASCII
);
249 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_column_number
, tvb
, offset
+8, 40, ENC_ASCII
);
251 proto_tree_add_item(sapigs_tables_tree
, hf_sapigs_table_column_width
, tvb
, offset
+8, 40, ENC_ASCII
);
253 is_table
= (char *)tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 4, ENC_ASCII
);
256 if ((data_length
> 0) && (tvb_reported_length_remaining(tvb
, offset
) >= data_length
)) {
257 proto_tree_add_item(sapigs_tree
, hf_sapigs_data
, tvb
, data_offset
, data_length
, ENC_NA
);
261 case 34:{ /* CHART */
262 proto_tree_add_item(sapigs_tree
, hf_sapigs_chart_config
, tvb
, offset
, 32, ENC_ASCII
|ENC_NA
);
264 proto_tree_add_item(sapigs_tree
, hf_sapigs_data
, tvb
, offset
, -1, ENC_NA
);
269 return tvb_reported_length(tvb
);
274 proto_register_sapigs(void)
276 static hf_register_info hf
[] = {
277 /* General Header fields */
278 { &hf_sapigs_function
,
279 { "Function", "sapigs.function", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
280 { &hf_sapigs_listener
,
281 { "Listener", "sapigs.listener", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
282 { &hf_sapigs_hostname
,
283 { "Hostname", "sapigs.hostname", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
285 { "Id", "sapigs.id", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
287 { "Padd1", "sapigs.padd1", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
289 { "Flag1", "sapigs.flag1", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
291 { "Padd2", "sapigs.padd2", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
293 { "Flag2", "sapigs.flag2", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
295 { "Padd3", "sapigs.padd3", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
298 { &hf_sapigs_eye_catcher
,
299 { "Eye catcher", "sapigs.eye_catcher", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
301 { "Padd4", "sapigs.padd4", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
302 { &hf_sapigs_codepage
,
303 { "Codepage", "sapigs.codepage", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
304 { &hf_sapigs_offset_data
,
305 { "Offset to data", "sapigs.offset_data", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
306 { &hf_sapigs_data_size
,
307 { "Data size", "sapigs.data_size", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
309 /* Portwatcher fields */
310 { &hf_sapigs_portwatcher
,
311 { "Portwatcher Port", "sapigs.portwatcher", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
312 { &hf_sapigs_portwatcher_version
,
313 { "Portwatcher version", "sapigs.portwatcher_version", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
314 { &hf_sapigs_portwatcher_info
,
315 { "Portwatcher Info", "sapigs.portwatcher_info", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
317 /* Interpreter information */
318 { &hf_sapigs_interpreter
,
319 { "Interpreter name", "sapigs.interpreter", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
320 { &hf_sapigs_chart_config
,
321 { "Chart configuration", "sapigs.chart_config", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
323 /* Table definition fields */
325 { "Table definition", "sapigs.tables", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
326 { &hf_sapigs_table_version
,
327 { "VERS", "sapigs.table_version", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Table version", HFILL
}},
328 { &hf_sapigs_table_name
,
329 { "TBNM", "sapigs.table_name", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Table name", HFILL
}},
330 { &hf_sapigs_table_line_number
,
331 { "TBLN", "sapigs.table_line_number", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Line count", HFILL
}},
332 { &hf_sapigs_table_width
,
333 { "TBWD", "sapigs.table_width", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Table width", HFILL
}},
334 { &hf_sapigs_table_column_name
,
335 { "TBCL", "sapigs.table_column_name", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Table column name", HFILL
}},
336 { &hf_sapigs_table_column_number
,
337 { "CLNM", "sapigs.table_column_number", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Column count", HFILL
}},
338 { &hf_sapigs_table_column_width
,
339 { "CLWD", "sapigs.table_column_width", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Column width", HFILL
}},
343 { "Data", "sapigs.table_data", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}}
346 /* Setup protocol subtre array */
347 static int *ett
[] = {
351 module_t
*sapigs_module
;
353 /* Register the protocol */
354 proto_sapigs
= proto_register_protocol("SAP Internet Graphic Server", "SAPIGS", "sapigs");
356 register_dissector("sapigs", dissect_sapigs
, proto_sapigs
);
358 proto_register_field_array(proto_sapigs
, hf
, array_length(hf
));
359 proto_register_subtree_array(ett
, array_length(ett
));
361 /* Register the preferences */
362 sapigs_module
= prefs_register_protocol(proto_sapigs
, proto_reg_handoff_sapigs
);
364 range_convert_str(wmem_epan_scope(), &global_sapigs_port_range
, SAPIGS_PORT_RANGE
, MAX_TCP_PORT
);
365 prefs_register_range_preference(sapigs_module
, "tcp_ports", "SAP IGS Protocol TCP port numbers", "Port numbers used for SAP IGS Protocol (default "SAPIGS_PORT_RANGE
")", &global_sapigs_port_range
, MAX_TCP_PORT
);
367 prefs_register_bool_preference(sapigs_module
, "highlight_unknow_items", "Highlight unknown SAP IGS messages", "Whether the SAP IGS Protocol dissector should highlight unknown IGS messages", &global_sapigs_highlight_items
);
372 * Helpers for dealing with the port range
374 static void range_delete_callback (uint32_t port
, void *ptr _U_
)
376 dissector_delete_uint("sapni.port", port
, sapigs_handle
);
379 static void range_add_callback (uint32_t port
, void *ptr _U_
)
381 dissector_add_uint("sapni.port", port
, sapigs_handle
);
385 * Register Hand off for the SAP IGS Protocol
388 proto_reg_handoff_sapigs(void)
390 static range_t
*sapigs_port_range
;
391 static bool initialized
= false;
394 sapigs_handle
= create_dissector_handle(dissect_sapigs
, proto_sapigs
);
397 range_foreach(sapigs_port_range
, range_delete_callback
, NULL
);
398 wmem_free(wmem_epan_scope(), sapigs_port_range
);
401 sapigs_port_range
= range_copy(wmem_epan_scope(), global_sapigs_port_range
);
402 range_foreach(sapigs_port_range
, range_add_callback
, NULL
);
406 * Editor modelines - https://www.wireshark.org/tools/modelines.html
411 * indent-tabs-mode: t
414 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
415 * :indentSize=8:tabSize=8:noTabs=false: