Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-sapigs.c
Line
Count
Source
1
/* packet-sapigs.c
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.
6
 *
7
 * Wireshark - Network traffic analyzer
8
 * By Gerald Combs <gerald@wireshark.org>
9
 * Copyright 1998 Gerald Combs
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
/*
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
18
 */
19
20
#include <config.h>
21
#include <inttypes.h>
22
#include <stdlib.h>
23
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>
29
30
/*
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.
33
 */
34
14
#define SAPIGS_PORT_RANGE "40000"
35
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 */
70
  /* NULL */
71
  { 0, NULL}
72
};
73
74
static int proto_sapigs;
75
76
/* Headers */
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;
86
87
/* Data */
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;
94
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;
104
105
/* Others */
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;
111
112
static int ett_sapigs;
113
114
/* Global port preference */
115
static range_t *global_sapigs_port_range;
116
117
/* Global highlight preference */
118
static bool global_sapigs_highlight_items = true;
119
120
/* Protocol handle */
121
static dissector_handle_t sapigs_handle;
122
123
void proto_reg_handoff_sapigs(void);
124
void proto_register_sapigs(void);
125
126
127
static int
128
dissect_sapigs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
129
1
{
130
1
  uint32_t offset = 0, err_val = 0;
131
1
  const char *data_length_str;
132
1
  unsigned data_offset = 0, data_length = 0;
133
1
  char *sapigs_info_function = NULL, *illbeback_type = NULL, *is_table = NULL;
134
1
  proto_item *ti = NULL, *sapigs_tables = NULL;
135
1
  proto_tree *sapigs_tree = NULL, *sapigs_tables_tree = NULL;
136
137
  /* Add the protocol to the column */
138
1
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "SAPIGS");
139
  /* Add function name in the info column */
140
1
  col_add_fstr(pinfo->cinfo, COL_INFO, " function: %s", tvb_get_string_enc(pinfo->pool, tvb, 0, 32, ENC_ASCII));
141
142
  /* Add the main sapigs subtree */
143
1
  ti = proto_tree_add_item(tree, proto_sapigs, tvb, 0, -1, ENC_NA);
144
1
  sapigs_tree = proto_item_add_subtree(ti, ett_sapigs);
145
146
  /* Retrieve function name */
147
1
  sapigs_info_function = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, 32, ENC_ASCII);
148
149
  /* Headers */
150
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_function, tvb, offset, 32, ENC_ASCII);
151
1
  offset += 32;
152
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_listener, tvb, offset, 32, ENC_ASCII);
153
1
  offset += 32;
154
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_hostname, tvb, offset, 81, ENC_ASCII);
155
1
  offset += 81;
156
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_id, tvb, offset, 4, ENC_ASCII);
157
1
  offset += 4;
158
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_padd1, tvb, offset, 15, ENC_ASCII);
159
1
  offset += 15;
160
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_flag1, tvb, offset, 1, ENC_ASCII);
161
1
  offset += 1;
162
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_padd2, tvb, offset, 20, ENC_ASCII);
163
1
  offset += 20;
164
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_flag2, tvb, offset, 1, ENC_ASCII);
165
1
  offset += 1;
166
1
  proto_tree_add_item(sapigs_tree, hf_sapigs_padd3, tvb, offset, 6, ENC_ASCII);
167
1
  offset += 6;
168
169
  /* switch over function name value */
170
1
  switch (str_to_val(sapigs_info_function, sapigs_function_lst, err_val)){
171
0
    case 8:{  /* ADM:PING */
172
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_portwatcher, tvb, offset, 5, ENC_ASCII);
173
0
      break;
174
0
    }
175
0
    case 9:{  /* ADM:PONG */
176
0
      break;
177
0
    }
178
0
    case 1:{  /* ADM:REGPW */
179
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_portwatcher, tvb, offset, 5, ENC_ASCII);
180
0
      offset += 32;
181
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_portwatcher_version, tvb, offset, 16, ENC_ASCII);
182
0
      break;
183
0
    }
184
0
    case 3:   /* ADM:REGIP */
185
0
    case 5:{  /* ADM:FREEIP */
186
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_portwatcher, tvb, offset, 5, ENC_ASCII);
187
0
      offset += 32;
188
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_interpreter, tvb, offset, 16, ENC_ASCII);
189
0
      offset += 32;
190
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_portwatcher_version, tvb, offset, 16, ENC_ASCII);
191
0
      offset += 32;
192
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_portwatcher_info, tvb, offset, 16, ENC_ASCII);
193
0
      break;
194
0
    }
195
0
    case 6:{  /* ADM:ILLBEBACK */
196
0
      illbeback_type = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, 10, ENC_ASCII|ENC_NA);
197
0
      if (strncmp("TransMagic", illbeback_type, 10) == 0){
198
        /* data is raw after eye_catcher */
199
0
        proto_tree_add_item(sapigs_tree, hf_sapigs_eye_catcher, tvb, offset, 10, ENC_ASCII);
200
0
        offset += 16;
201
0
        proto_tree_add_item(sapigs_tree, hf_sapigs_data, tvb, offset, -1, ENC_NA);
202
0
      } else {
203
        /* we receive sized data */
204
0
        proto_tree_add_item_ret_string(sapigs_tree, hf_sapigs_data_size, tvb, offset, 5, ENC_ASCII, pinfo->pool, (const uint8_t**)&data_length_str);
205
0
        ws_strtou((char *)data_length_str, NULL, &data_length);
206
0
        offset += 5;
207
        /* Data */
208
0
        if ((data_length > 0) && (tvb_reported_length_remaining(tvb, offset) >= data_length)) {
209
0
          proto_tree_add_item(sapigs_tree, hf_sapigs_data, tvb, offset, data_length, ENC_NA);
210
0
        }
211
0
      }
212
0
      break;
213
0
    }
214
0
    case 30:  /* ZIPPER */
215
0
    case 31:  /* IMGCONV */
216
0
    case 33:  /* XMLCHART */
217
0
    case 16:{ /* ADM:GETDUMP */
218
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_eye_catcher, tvb, offset, 10, ENC_ASCII);
219
0
      offset += 10;
220
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_padd4, tvb, offset, 2, ENC_ASCII);
221
0
      offset += 2;
222
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_codepage, tvb, offset, 4, ENC_ASCII);
223
0
      offset += 4;
224
      /* Data offset */
225
0
      proto_tree_add_item_ret_string(sapigs_tree, hf_sapigs_offset_data, tvb, offset, 16, ENC_ASCII, pinfo->pool, (const uint8_t**)&data_length_str);
226
0
      ws_strtou(data_length_str, NULL, &data_offset);
227
0
      offset += 16;
228
      /* Data length */
229
0
      proto_tree_add_item_ret_string(sapigs_tree, hf_sapigs_data_size, tvb, offset, 5, ENC_ASCII, pinfo->pool, (const uint8_t**)&data_length_str);
230
0
      ws_strtou((char *)data_length_str, NULL, &data_length);
231
0
      offset += 16;
232
0
      data_offset += offset;
233
      /* Definition tables */
234
0
      is_table = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, 4, ENC_ASCII);
235
      /* if the 4 next char is VERS, we are at the beginning of one definition table */
236
0
      while(strncmp("VERS", is_table, 4) == 0){
237
        /* Build a tree for Tables */
238
0
        sapigs_tables = proto_tree_add_item(sapigs_tree, hf_sapigs_tables, tvb, offset, 336, ENC_NA);
239
0
        sapigs_tables_tree = proto_item_add_subtree(sapigs_tables, ett_sapigs);
240
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_version, tvb, offset+8, 40, ENC_ASCII);
241
0
        offset += 48;
242
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_name, tvb, offset+8, 40, ENC_ASCII);
243
0
        offset += 48;
244
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_line_number, tvb, offset+8, 40, ENC_ASCII);
245
0
        offset += 48;
246
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_width, tvb, offset+8, 40, ENC_ASCII);
247
0
        offset += 48;
248
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_column_name, tvb, offset+8, 40, ENC_ASCII);
249
0
        offset += 48;
250
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_column_number, tvb, offset+8, 40, ENC_ASCII);
251
0
        offset += 48;
252
0
        proto_tree_add_item(sapigs_tables_tree, hf_sapigs_table_column_width, tvb, offset+8, 40, ENC_ASCII);
253
0
        offset += 48;
254
0
        is_table = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, 4, ENC_ASCII);
255
0
      }
256
      /* Data */
257
0
      if ((data_length > 0) && (tvb_reported_length_remaining(tvb, offset) >= data_length)) {
258
0
        proto_tree_add_item(sapigs_tree, hf_sapigs_data, tvb, data_offset, data_length, ENC_NA);
259
0
      }
260
0
      break;
261
0
    }
262
0
    case 34:{ /* CHART */
263
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_chart_config, tvb, offset, 32, ENC_ASCII);
264
0
      offset += 32;
265
0
      proto_tree_add_item(sapigs_tree, hf_sapigs_data, tvb, offset, -1, ENC_NA);
266
0
      break;
267
0
    }
268
1
  }
269
270
1
  return tvb_reported_length(tvb);
271
1
}
272
273
274
void
275
proto_register_sapigs(void)
276
14
{
277
14
  static hf_register_info hf[] = {
278
    /* General Header fields */
279
14
    { &hf_sapigs_function,
280
14
      { "Function", "sapigs.function", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
281
14
    { &hf_sapigs_listener,
282
14
      { "Listener", "sapigs.listener", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
283
14
    { &hf_sapigs_hostname,
284
14
      { "Hostname", "sapigs.hostname", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
285
14
    { &hf_sapigs_id,
286
14
      { "Id", "sapigs.id", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
287
14
    { &hf_sapigs_padd1,
288
14
      { "Padd1", "sapigs.padd1", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
289
14
    { &hf_sapigs_flag1,
290
14
      { "Flag1", "sapigs.flag1", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
291
14
    { &hf_sapigs_padd2,
292
14
      { "Padd2", "sapigs.padd2", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
293
14
    { &hf_sapigs_flag2,
294
14
      { "Flag2", "sapigs.flag2", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
295
14
    { &hf_sapigs_padd3,
296
14
      { "Padd3", "sapigs.padd3", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
297
298
    /* Data headers */
299
14
    { &hf_sapigs_eye_catcher,
300
14
      { "Eye catcher", "sapigs.eye_catcher", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
301
14
    { &hf_sapigs_padd4,
302
14
      { "Padd4", "sapigs.padd4", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
303
14
    { &hf_sapigs_codepage,
304
14
      { "Codepage", "sapigs.codepage", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
305
14
    { &hf_sapigs_offset_data,
306
14
      { "Offset to data", "sapigs.offset_data", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
307
14
    { &hf_sapigs_data_size,
308
14
      { "Data size", "sapigs.data_size", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
309
310
    /* Portwatcher fields */
311
14
    { &hf_sapigs_portwatcher,
312
14
      { "Portwatcher Port", "sapigs.portwatcher", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
313
14
    { &hf_sapigs_portwatcher_version,
314
14
      { "Portwatcher version", "sapigs.portwatcher_version", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
315
14
    { &hf_sapigs_portwatcher_info,
316
14
      { "Portwatcher Info", "sapigs.portwatcher_info", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
317
318
    /* Interpreter information */
319
14
    { &hf_sapigs_interpreter,
320
14
      { "Interpreter name", "sapigs.interpreter", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
321
14
    { &hf_sapigs_chart_config,
322
14
      { "Chart configuration", "sapigs.chart_config", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
323
324
    /* Table definition fields */
325
14
    { &hf_sapigs_tables,
326
14
      { "Table definition", "sapigs.tables", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
327
14
    { &hf_sapigs_table_version,
328
14
      { "VERS", "sapigs.table_version", FT_STRING, BASE_NONE, NULL, 0x0, "Table version", HFILL }},
329
14
    { &hf_sapigs_table_name,
330
14
      { "TBNM", "sapigs.table_name", FT_STRING, BASE_NONE, NULL, 0x0, "Table name", HFILL }},
331
14
    { &hf_sapigs_table_line_number,
332
14
      { "TBLN", "sapigs.table_line_number", FT_STRING, BASE_NONE, NULL, 0x0, "Line count", HFILL }},
333
14
    { &hf_sapigs_table_width,
334
14
      { "TBWD", "sapigs.table_width", FT_STRING, BASE_NONE, NULL, 0x0, "Table width", HFILL }},
335
14
    { &hf_sapigs_table_column_name,
336
14
      { "TBCL", "sapigs.table_column_name", FT_STRING, BASE_NONE, NULL, 0x0, "Table column name", HFILL }},
337
14
    { &hf_sapigs_table_column_number,
338
14
      { "CLNM", "sapigs.table_column_number", FT_STRING, BASE_NONE, NULL, 0x0, "Column count", HFILL }},
339
14
    { &hf_sapigs_table_column_width,
340
14
      { "CLWD", "sapigs.table_column_width", FT_STRING, BASE_NONE, NULL, 0x0, "Column width", HFILL }},
341
342
    /* Data */
343
14
    { &hf_sapigs_data,
344
14
      { "Data", "sapigs.table_data", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}
345
14
  };
346
347
  /* Setup protocol subtre array */
348
14
  static int *ett[] = {
349
14
    &ett_sapigs
350
14
  };
351
352
14
  module_t *sapigs_module;
353
354
  /* Register the protocol */
355
14
  proto_sapigs = proto_register_protocol("SAP Internet Graphic Server", "SAPIGS", "sapigs");
356
357
14
  register_dissector("sapigs", dissect_sapigs, proto_sapigs);
358
359
14
  proto_register_field_array(proto_sapigs, hf, array_length(hf));
360
14
  proto_register_subtree_array(ett, array_length(ett));
361
362
  /* Register the preferences */
363
14
  sapigs_module = prefs_register_protocol(proto_sapigs, proto_reg_handoff_sapigs);
364
365
14
  range_convert_str(wmem_epan_scope(), &global_sapigs_port_range, SAPIGS_PORT_RANGE, MAX_TCP_PORT);
366
14
  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
368
14
  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);
369
370
14
}
371
372
/**
373
 * Helpers for dealing with the port range
374
 */
375
static void range_delete_callback (uint32_t port, void *ptr _U_)
376
0
{
377
0
        dissector_delete_uint("sapni.port", port, sapigs_handle);
378
0
}
379
380
static void range_add_callback (uint32_t port, void *ptr _U_)
381
14
{
382
14
        dissector_add_uint("sapni.port", port, sapigs_handle);
383
14
}
384
385
/**
386
 * Register Hand off for the SAP IGS Protocol
387
 */
388
void
389
proto_reg_handoff_sapigs(void)
390
14
{
391
14
  static range_t *sapigs_port_range;
392
14
  static bool initialized = false;
393
394
14
  if (!initialized) {
395
14
    sapigs_handle = create_dissector_handle(dissect_sapigs, proto_sapigs);
396
14
    initialized = true;
397
14
  } else {
398
0
    range_foreach(sapigs_port_range, range_delete_callback, NULL);
399
0
    wmem_free(wmem_epan_scope(), sapigs_port_range);
400
0
  }
401
402
14
  sapigs_port_range = range_copy(wmem_epan_scope(), global_sapigs_port_range);
403
  range_foreach(sapigs_port_range, range_add_callback, NULL);
404
14
}
405
406
/*
407
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
408
 *
409
 * Local variables:
410
 * c-basic-offset: 8
411
 * tab-width: 8
412
 * indent-tabs-mode: t
413
 * End:
414
 *
415
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
416
 * :indentSize=8:tabSize=8:noTabs=false:
417
 */